-
-
Notifications
You must be signed in to change notification settings - Fork 308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add GUI #2375
base: dev/1.4
Are you sure you want to change the base?
Add GUI #2375
Conversation
packages/core/src/ui/UIUtils.ts
Outdated
const { elements: projectE } = virtualCamera.projectionMatrix; | ||
const { elements: viewE } = virtualCamera.viewMatrix; | ||
(projectE[0] = 2 / canvas.width), (projectE[5] = 2 / canvas.height), (projectE[10] = 0); | ||
rhi.activeRenderTarget(null, new Vector4(0, 0, 1, 1), renderContext.flipProjection, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will create Vector4 every frame
(row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); | ||
row[3] = width - expectWidth * (1 - right) * widthScale; | ||
} else { | ||
row = [expectWidth * left, fixedLeft, width - fixedRight, width - expectWidth * (1 - right)]; | ||
(row[0] = expectWidth * left), (row[1] = fixedLeft), (row[2] = width - fixedRight); | ||
row[3] = width - expectWidth * (1 - right); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can not agree more
packages/core/src/ui/UIRenderer.ts
Outdated
import { IUIElement } from "./interface/IUIElement"; | ||
|
||
@dependentComponents(UITransform, DependentMode.AutoAdd) | ||
export class UIRenderer extends Renderer implements IUIElement { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should add implements ISpriteRenderer, as follows:
export class UIRenderer extends Renderer implements IUIElement, ISpriteRenderer {}
packages/core/src/ui/UIImage.ts
Outdated
const texture = this.sprite?.texture; | ||
if (!texture) { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be more appropriate to check the texture first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Outside diff range and nitpick comments (4)
packages/core/src/2d/text/TextUtils.ts (2)
274-274
: LGTM with documentation suggestionThe method signature change is correct. Consider adding JSDoc to document the new parameter type.
+/** + * Measures text without wrapping for both TextRenderer and UIText. + * @param renderer - The text renderer (TextRenderer or UIText) + * @returns TextMetrics containing measurement information + */ static measureTextWithoutWrap(renderer: TextRenderer | UIText): TextMetrics {
Line range hint
100-274
: Consider adding integration tests for UIText measurementsSince these methods now support both TextRenderer and UIText, it would be valuable to add integration tests that verify:
- Text measurement consistency between TextRenderer and UIText
- Edge cases with different text styles and overflow modes
- Performance implications of supporting both types
This will help ensure compatibility and prevent regressions.
packages/core/src/ui/UIText.ts (2)
323-323
: Address the TODO: Optimize world positions buffer updateThere's a TODO comment at line 323 indicating that optimization is needed:
//@todo: Always update world positions to buffer, should opt
. Would you like assistance in implementing this optimization or opening a GitHub issue to track it?
514-514
: Consider splitting assignments for better readabilityAt line 514, the line combines multiple assignments in one statement:
const charRenderInfo = (charRenderInfos[renderElementCount++] = charRenderInfoPool.get());This reduces code clarity. Consider splitting the assignments.
Suggested change:
- const charRenderInfo = (charRenderInfos[renderElementCount++] = charRenderInfoPool.get()); + const charRenderInfo = charRenderInfoPool.get(); + charRenderInfos[renderElementCount++] = charRenderInfo;🧰 Tools
🪛 Biome
[error] 514-514: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
packages/core/src/2d/text/TextUtils.ts
(3 hunks)packages/core/src/ui/UIText.ts
(1 hunks)packages/core/src/ui/index.ts
(1 hunks)
🧰 Additional context used
🪛 Biome
packages/core/src/ui/UIText.ts
[error] 274-274: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 513-513: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 514-514: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 524-524: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 526-526: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 608-608: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 610-610: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
🪛 GitHub Check: lint
packages/core/src/ui/index.ts
[failure] 10-10:
Delete ⏎
🔇 Additional comments (2)
packages/core/src/2d/text/TextUtils.ts (2)
3-3
: LGTM: Import addition is correct
The UIText import is properly placed and supports the new type parameter additions.
100-100
: Verify type compatibility between TextRenderer and UIText
The method now accepts both TextRenderer and UIText types. Please ensure that UIText implements all required properties and methods:
- _getSubFont()
- width
- height
- lineSpacing
- overflowMode
✅ Verification successful
Let me gather more information about the UIText class implementation to verify the required properties and methods.
Let me verify one last aspect - the inheritance hierarchy and interface implementation of UIText and TextRenderer.
Based on the gathered evidence, I can now provide the final response:
Type compatibility between TextRenderer and UIText is verified
The verification confirms that UIText implements all required properties and methods:
_getSubFont()
method is properly implemented- Required properties are present:
_width
,_height
,_lineSpacing
,_overflowMode
- Both classes extend from base rendering classes (UIRenderer and Renderer respectively) and share similar structure
- Both classes have compatible implementations for text rendering functionality
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify UIText implements required properties and methods
# Search for UIText class definition and implementation
ast-grep --pattern 'class UIText {
$$$
_getSubFont() {
$$$
}
$$$
}'
# Search for width, height, lineSpacing, and overflowMode properties
rg -A 5 'class UIText'
Length of output: 599
Script:
#!/bin/bash
# Search for required properties and methods in UIText class
rg -A 2 "(_getSubFont|width\s*:|height\s*:|lineSpacing\s*:|overflowMode\s*:)" packages/core/src/ui/UIText.ts
# Search for property declarations
ast-grep --pattern 'class UIText extends UIRenderer {
$$$
private _width: $_
$$$
}'
# Check if UIText extends TextRenderer or has similar interface
rg -A 2 "interface.*Text" packages/core/src/2d/text/
Length of output: 1562
Script:
#!/bin/bash
# Check TextRenderer class and its properties
rg -A 5 "class TextRenderer" packages/core/src/2d/text/
# Check if UIText implements or extends any text-related interfaces
rg "class UIText.*implements" packages/core/src/ui/UIText.ts
# Look for any shared interfaces between TextRenderer and UIText
ast-grep --pattern 'interface $_ {
$$$
_getSubFont(): $_
$$$
}'
Length of output: 825
packages/core/src/ui/UIText.ts
Outdated
//@ts-ignore | ||
this._color._onValueChanged = this._onColorChange.bind(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid using //@ts-ignore
by properly typing properties
At lines 256-257, the use of //@ts-ignore
suppresses TypeScript errors, which might hide potential issues. Consider properly typing the _onValueChanged
property in the Color
class to avoid the need for //@ts-ignore
.
packages/core/src/ui/UIText.ts
Outdated
const { r, g, b, a } = this.color; | ||
const tempIndices = CharRenderInfo.triangles; | ||
const tempIndicesLength = tempIndices.length; | ||
const subChunk = (textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid nested assignments within expressions
At line 608, the assignments are nested within an expression:
const subChunk = (textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4));
This can reduce code readability. Consider separating the assignments.
Suggested change:
- const subChunk = (textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4));
+ textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4);
+ const subChunk = textChunk.subChunk;
🧰 Tools
🪛 Biome
[error] 608-608: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
const tempIndicesLength = tempIndices.length; | ||
const subChunk = (textChunk.subChunk = this._getChunkManager().allocateSubChunk(count * 4)); | ||
const vertices = subChunk.chunk.vertices; | ||
const indices = (subChunk.indices = []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid nested assignments within expressions
At line 610, the assignment is nested within an expression:
const indices = (subChunk.indices = []);
This can be confusing. Consider assigning to subChunk.indices
first, then referencing it.
Suggested change:
- const indices = (subChunk.indices = []);
+ subChunk.indices = [];
+ const indices = subChunk.indices;
🧰 Tools
🪛 Biome
[error] 610-610: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
const char = line[j]; | ||
const charInfo = charFont._getCharInfo(char); | ||
if (charInfo.h > 0) { | ||
firstRow < 0 && (firstRow = j); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions to improve code clarity
At line 513, the assignment firstRow = j
is performed within a logical AND expression. This can be confusing and reduce readability. It's better to use an explicit if
statement.
Suggested change:
- firstRow < 0 && (firstRow = j);
+ if (firstRow < 0) {
+ firstRow = j;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
firstRow < 0 && (firstRow = j); | |
if (firstRow < 0) { | |
firstRow = j; | |
} |
🧰 Tools
🪛 Biome
[error] 513-513: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
localPositions.set(left, top, right, bottom); | ||
i === firstLine && (maxY = Math.max(maxY, top)); | ||
minY = Math.min(minY, bottom); | ||
j === firstRow && (minX = Math.min(minX, left)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions for better code readability
At line 526, the assignment minX = Math.min(minX, left)
is inside a logical AND expression. For clarity, use an explicit if
statement.
Suggested change:
- j === firstRow && (minX = Math.min(minX, left));
+ if (j === firstRow) {
+ minX = Math.min(minX, left);
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
j === firstRow && (minX = Math.min(minX, left)); | |
if (j === firstRow) { | |
minX = Math.min(minX, left); | |
} |
🧰 Tools
🪛 Biome
[error] 526-526: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
const top = startY + ascent; | ||
const bottom = startY - descent; | ||
localPositions.set(left, top, right, bottom); | ||
i === firstLine && (maxY = Math.max(maxY, top)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions for code clarity
At line 524, the assignment maxY = Math.max(maxY, top)
is performed within a logical AND expression. This can make the code less readable. Use an explicit if
statement instead.
Suggested change:
- i === firstLine && (maxY = Math.max(maxY, top));
+ if (i === firstLine) {
+ maxY = Math.max(maxY, top);
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
i === firstLine && (maxY = Math.max(maxY, top)); | |
if (i === firstLine) { | |
maxY = Math.max(maxY, top); | |
} |
🧰 Tools
🪛 Biome
[error] 524-524: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIText.ts
Outdated
this._freeTextChunks(); | ||
this._textChunks = null; | ||
|
||
this._subFont && (this._subFont = null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions for better readability
At line 274, the assignment this._subFont = null
is used within a logical AND expression. This can make the code harder to read and maintain. Consider using an explicit if
statement for clarity.
Suggested change:
- this._subFont && (this._subFont = null);
+ if (this._subFont) {
+ this._subFont = null;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
this._subFont && (this._subFont = null); | |
if (this._subFont) { | |
this._subFont = null; | |
} |
🧰 Tools
🪛 Biome
[error] 274-274: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
* feat: add button
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 28
🧹 Outside diff range and nitpick comments (46)
packages/core/src/ui/interactive/transition/TransitionType.ts (1)
1-6
: Consider adding JSDoc comments to document each transition type.Adding documentation would improve maintainability by explaining the purpose and behavior of each transition type.
+/** + * Defines the types of transitions available for UI elements. + */ export enum TransitionType { + /** No transition effect */ None, + /** Color-based transition effect */ Color, + /** Sprite-based transition effect */ Sprite, + /** Scale-based transition effect */ Scale }packages/core/src/ui/interactive/InteractiveState.ts (2)
1-7
: Consider renaming 'Disable' to 'Disabled' for grammatical consistency.The enum implementation looks good and covers all essential UI interaction states. However, 'Disable' should be 'Disabled' to maintain consistent grammar (adjective form) with other state names.
export enum InteractiveState { None, Normal, Pressed, Hover, - Disable + Disabled }
1-1
: Add JSDoc documentation for the enum.Since this enum is a core part of the GUI infrastructure used across multiple components, please add JSDoc documentation explaining:
- The purpose of each state
- Usage examples
- State transition expectations
+/** + * Represents the interactive states of UI components. + * + * @remarks + * - None: Initial/unset state + * - Normal: Default interactive state + * - Pressed: Component is being actively pressed/clicked + * - Hover: Mouse is hovering over the component + * - Disabled: Component is non-interactive + */ export enum InteractiveState {packages/core/src/ui/interface/IGroupElement.ts (2)
4-8
: Add JSDoc documentation and consider using readonly modifiers.The interface definition could benefit from documentation explaining its purpose and usage. Additionally, consider marking properties that shouldn't change after initialization as readonly.
Here's the suggested improvement:
+/** + * Interface for elements that can be part of a UIGroup. + * Extends ICanvasElement to provide group-specific functionality. + */ export interface IGroupElement extends ICanvasElement { - _group: UIGroup; - _indexInGroup: number; + readonly _group: UIGroup; + readonly _indexInGroup: number; _onGroupModify(flag: GroupModifyFlags): void; }
4-8
: Consider encapsulating internal implementation details.The interface exposes implementation details through underscore-prefixed properties and methods. This could make it harder to maintain and refactor the codebase in the future.
Consider:
- Moving internal details to a separate internal interface
- Using a more public-facing API design
- Using TypeScript's private fields (#) if these are truly meant to be private
Example approach:
export interface IGroupElement extends ICanvasElement { readonly group: UIGroup; readonly indexInGroup: number; onModify(flag: GroupModifyFlags): void; } // Internal interface for implementation interface IGroupElementInternal extends IGroupElement { _group: UIGroup; _indexInGroup: number; _onGroupModify(flag: GroupModifyFlags): void; }packages/core/src/ui/interface/IUIGraphics.ts (1)
5-10
: Consider interface segregation for better modularity.The current interface combines multiple responsibilities (depth management, hit testing, and group behavior). Consider splitting this into more focused interfaces:
interface IDepthManagement { depth: number; } interface IRaycastable { raycastEnable: boolean; raycastPadding: Vector4; raycast(ray: Ray, out: HitResult, distance: number): boolean; } export interface IUIGraphics extends IGroupElement, IDepthManagement, IRaycastable {}This would:
- Make it easier to implement each behavior independently
- Allow for better composition of UI elements
- Improve testability of each concern
Would you like me to create a GitHub issue to track this refactoring suggestion?
packages/core/src/ui/index.ts (2)
1-15
: LGTM! Well-organized exports structure.The exports are logically grouped and follow a clear pattern, providing a good foundation for the GUI infrastructure. The separation between core enums, UI components, and transitions is clear and maintainable.
Consider adding JSDoc comments to document the main export groups:
+/** Core UI enums */ export { UIGroup } from "./UIGroup"; export { CanvasRenderMode } from "./enums/CanvasRenderMode"; export { ResolutionAdaptationStrategy } from "./enums/ResolutionAdaptationStrategy"; +/** UI Components */ export { Button } from "./Button"; export { UICanvas } from "./UICanvas"; export { UIImage } from "./UIImage"; export { UIRenderer } from "./UIRenderer"; export { UIText } from "./UIText"; export { UITransform } from "./UITransform"; +/** Transition Components */ export { ColorTransition } from "./interactive/transition/ColorTransition"; export { ScaleTransition } from "./interactive/transition/ScaleTransition"; export { SpriteTransition } from "./interactive/transition/SpriteTransition"; export { Transition } from "./interactive/transition/Transition";
5-15
: Consider sorting exports within groups.While the current organization is clear, consider sorting the exports alphabetically within their groups to make it easier to locate specific exports as the list grows.
export { Button } from "./Button"; -export { UICanvas } from "./UICanvas"; -export { UIImage } from "./UIImage"; -export { UIRenderer } from "./UIRenderer"; -export { UIText } from "./UIText"; -export { UITransform } from "./UITransform"; +export { UICanvas } from "./UICanvas"; +export { UIImage } from "./UIImage"; +export { UIRenderer } from "./UIRenderer"; +export { UIText } from "./UIText"; +export { UITransform } from "./UITransform"; -export { ColorTransition } from "./interactive/transition/ColorTransition"; -export { ScaleTransition } from "./interactive/transition/ScaleTransition"; -export { SpriteTransition } from "./interactive/transition/SpriteTransition"; -export { Transition } from "./interactive/transition/Transition"; +export { ColorTransition } from "./interactive/transition/ColorTransition"; +export { ScaleTransition } from "./interactive/transition/ScaleTransition"; +export { SpriteTransition } from "./interactive/transition/SpriteTransition"; +export { Transition } from "./interactive/transition/Transition";packages/core/src/ui/interactive/transition/ScaleTransition.ts (2)
5-12
: Consider enhancing configurability and visual feedback.The current implementation could benefit from:
- Constructor parameters to allow customization of scale values and duration
- Different scale values for hover and disabled states to provide better visual feedback
Consider this implementation:
- constructor() { + constructor(config?: { + normal?: number; + hover?: number; + pressed?: number; + disabled?: number; + duration?: number; + }) { super(); - this._normal = 1; - this._hover = 1; - this._pressed = 1.2; - this._disabled = 1; - this._duration = 0.1; + this._normal = config?.normal ?? 1; + this._hover = config?.hover ?? 1.1; + this._pressed = config?.pressed ?? 1.2; + this._disabled = config?.disabled ?? 0.9; + this._duration = config?.duration ?? 0.2; }
18-20
: Consider using easing functions for smoother transitions.The current linear interpolation might not provide the most visually pleasing transition. Consider implementing easing functions for smoother animations.
Example implementation:
protected override _updateCurrentValue(srcValue: number, destValue: number, weight: number): void { // Ease in-out quad function const easeWeight = weight < 0.5 ? 2 * weight * weight : 1 - Math.pow(-2 * weight + 2, 2) / 2; this._currentValue = (destValue - srcValue) * easeWeight + srcValue; }packages/core/src/ui/interactive/transition/ColorTransition.ts (2)
6-15
: Consider documenting color states and extracting constants.The color states and transition duration would benefit from documentation and constant definitions.
Consider applying these improvements:
+/** Default transition duration in seconds */ +private static readonly DEFAULT_DURATION = 0.1; + +/** Default color states for different interaction states */ +private static readonly COLORS = { + NORMAL: new Color(1, 1, 1, 1), + HOVER: new Color(245 / 255, 245 / 255, 245 / 255, 1), + PRESSED: new Color(200 / 255, 200 / 255, 200 / 255, 1), + DISABLED: new Color(200 / 255, 200 / 255, 200 / 255, 1) +} as const; constructor() { super(); - this._normal = new Color(1, 1, 1, 1); - this._hover = new Color(245 / 255, 245 / 255, 245 / 255, 1); - this._pressed = new Color(200 / 255, 200 / 255, 200 / 255, 1); - this._disabled = new Color(200 / 255, 200 / 255, 200 / 255, 1); - this._duration = 0.1; + this._normal = ColorTransition.COLORS.NORMAL.clone(); + this._hover = ColorTransition.COLORS.HOVER.clone(); + this._pressed = ColorTransition.COLORS.PRESSED.clone(); + this._disabled = ColorTransition.COLORS.DISABLED.clone(); + this._duration = ColorTransition.DEFAULT_DURATION; this._currentValue = new Color(); }
17-29
: Consider optimizing color object reuse.The current implementation reuses the color object effectively in _getTargetValueCopy, but _updateCurrentValue could be optimized further.
Consider this optimization:
protected override _updateCurrentValue(srcValue: Color, destValue: Color, weight: number): void { if (weight >= 1) { this._currentValue.copyFrom(destValue); } else { - Color.lerp(srcValue, destValue, weight, this._currentValue); + // Avoid potential allocation in Color.lerp by ensuring _currentValue is always used + this._currentValue.r = srcValue.r + (destValue.r - srcValue.r) * weight; + this._currentValue.g = srcValue.g + (destValue.g - srcValue.g) * weight; + this._currentValue.b = srcValue.b + (destValue.b - srcValue.b) * weight; + this._currentValue.a = srcValue.a + (destValue.a - srcValue.a) * weight; } }packages/core/src/ui/Button.ts (3)
6-6
: Consider adding JSDoc for the listeners property.While the property is well-typed and properly encapsulated, adding JSDoc would improve maintainability by documenting the purpose of this collection.
+ /** Collection of click event listeners managed using SafeLoopArray for safe iteration. */ private _listeners: SafeLoopArray<IUIListener> = new SafeLoopArray<IUIListener>();
8-14
: Consider preventing duplicate listener registrations.The current implementation allows the same listener to be registered multiple times, which could lead to redundant executions.
addClicked(listener: (event: PointerEventData) => void): void { + if (this._listeners.find((value) => value.fn === listener)) { + return; + } this._listeners.push({ fn: listener }); }
38-41
: Consider adding JSDoc to document the interface.While the interface is well-structured, adding documentation would improve maintainability.
+/** + * Interface for UI event listeners with destruction tracking. + */ export interface IUIListener { + /** The event handling function */ fn: (event: PointerEventData) => void; + /** Flag indicating if the listener has been destroyed */ destroyed?: boolean; }packages/core/src/input/pointer/emitter/PointerPhysicsEventEmitter.ts (3)
13-49
: Consider breaking down the complex raycast processing logic.The method contains nested loops and multiple conditions that could be extracted into smaller, focused methods for better maintainability.
Consider refactoring into smaller methods:
private _isValidScene(scene: Scene): boolean { return scene.isActive && !scene.destroyed; } private _isPointerInViewport(x: number, y: number, viewport: { x: number, y: number, width: number, height: number }): boolean { return !(x < viewport.x || y < viewport.y || x > viewport.x + viewport.width || y > viewport.y + viewport.height); } private _processCamera(camera: Camera, ray: Ray, pointer: Pointer): boolean { if (camera.renderTarget) return false; const { x, y } = pointer.position; if (!this._isPointerInViewport(x, y, camera.pixelViewport)) { return false; } camera.screenPointToRay(pointer.position, ray); return true; }
79-96
: Consider caching event data to reduce object creation.The method creates multiple event data objects with the same parameters. Consider caching the event data for better performance.
private _processUp(pointer: Pointer): void { const { _enteredEntity: enteredEntity, _draggedEntity: draggedEntity } = this; if (enteredEntity) { const sameTarget = this._pressedEntity === enteredEntity; const eventData = this._createEventData(pointer, enteredEntity, enteredEntity); this._invokeEntityScripts(enteredEntity, (script: Script) => { script.onPointerUp?.(eventData); sameTarget && script.onPointerClick?.(eventData); script.onPointerDrop?.(eventData); }); } // ... rest of the method }
98-115
: Apply similar event data caching optimization.Similar to the _processUp method, consider caching the event data objects to reduce object creation.
packages/core/src/ui/UIUtils.ts (4)
21-37
: Add JSDoc documentation for better maintainability.This method handles complex parent traversal and listener management. Consider adding JSDoc documentation to explain:
- The purpose of the method
- The relationship between entities and listeners
- The significance of the root canvas check
47-82
: Consider extracting shared logic between registration methods.The
registerElementToCanvas
andregisterElementToGroup
methods share similar patterns for managing indices and elements. Consider extracting the common logic into a private helper method to improve maintainability and reduce code duplication.Example approach:
private static registerElementToContainer<T extends ICanvasElement | IGroupElement, C extends UICanvas | UIGroup>( element: T, container: C, getPreContainer: (el: T) => C, getElements: (c: C) => DisorderedArray<T>, updateIndex: (el: T, index: number) => void ): void { const preContainer = getPreContainer(element); if (preContainer !== container) { // ... shared logic here } }🧰 Tools
🪛 Biome
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
84-126
: Consider using type guard functions for safer type casting.Replace type casting with type guard functions to improve type safety and readability:
private static isUICanvas(component: Component): component is UICanvas { return component._componentType === ComponentType.UICanvas; } private static isUIGroup(component: Component): component is UIGroup { return component._componentType === ComponentType.UIGroup; }Then use them in the methods:
if (component.enabled && this.isUICanvas(component) && component._isRootCanvas) { return component; }
136-143
: Add comments explaining matrix transformations.The matrix operations for projection and view transformations would benefit from documentation explaining:
- The purpose of each transformation
- The coordinate space being used
- The significance of the magic numbers (2, 0)
packages/core/src/ui/UIRenderer.ts (2)
59-61
: Consider omitting redundant type annotations.The type annotations can be inferred from the initializers and can be omitted for better conciseness.
Apply these changes:
-protected _alpha: number = 1; +protected _alpha = 1; -protected _color: Color = new Color(1, 1, 1, 1); +protected _color = new Color(1, 1, 1, 1);
229-241
: Improve variable naming in _hitTest method.Consider using more descriptive variable names for better readability.
Apply these changes:
protected _hitTest(localPosition: Vector3): boolean { const { x, y } = localPosition; const uiTransform = <UITransform>this._transform; - const { x: width, y: height } = uiTransform.size; - const { x: pivotX, y: pivotY } = uiTransform.pivot; + const { x: elementWidth, y: elementHeight } = uiTransform.size; + const { x: pivotOffsetX, y: pivotOffsetY } = uiTransform.pivot; const { x: paddingLeft, y: paddingBottom, z: paddingRight, w: paddingTop } = this.raycastPadding; return ( - x >= -width * pivotX + paddingLeft && - x <= width * (1 - pivotX) - paddingRight && - y >= -height * pivotY + paddingTop && - y <= height * (1 - pivotY) - paddingBottom + x >= -elementWidth * pivotOffsetX + paddingLeft && + x <= elementWidth * (1 - pivotOffsetX) - paddingRight && + y >= -elementHeight * pivotOffsetY + paddingTop && + y <= elementHeight * (1 - pivotOffsetY) - paddingBottom ); }packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (3)
223-232
: Improve readability and maintainability of path compositionSeveral improvements can be made to the
_composedPath
method:
- Avoid assignment within expressions
- Add early return for invalid input
- Consider extracting the path composition logic to a separate method for better reusability
Apply this diff to improve the code:
private _composedPath(element: Component, path: Entity[]): Entity[] { if (!element) { path.length = 0; return path; } - let entity = (path[0] = element._entity); + path[0] = element._entity; + let entity = element._entity; let i = 1; if (element._componentType === ComponentType.UICanvas && (<UICanvas>element)._isRootCanvas) { path.length = 1; return path; } else { const rootEntity = (<UICanvas | UIRenderer>element)._rootCanvas._entity; for (; i < PointerUIEventEmitter._MAX_PATH_DEPTH && !!entity && entity !== rootEntity; i++) { entity = path[i] = entity.parent; } } path.length = i; return path; }🧰 Tools
🪛 Biome
[error] 223-223: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
170-171
: Consider performance optimizations for path operationsThe current implementation creates new paths and performs comparisons frequently, which could impact performance with high-frequency pointer events. Consider:
- Caching paths when possible
- Using a more efficient data structure for path comparison
- Implementing path pooling to reduce garbage collection
13-21
: Enhance documentation and type safetyThe class could benefit from:
- JSDoc documentation for the class explaining its purpose and usage
- Type annotations for event handlers
- Documentation of the relationship between different pointer states
- Interface definitions for the event data structure
packages/core/src/input/pointer/PointerManager.ts (5)
4-12
: Well-structured architectural improvements!The introduction of
ClearableObjectPool
and event emitters shows good separation of concerns and memory management practices. The modular approach with separate emitter classes will make the code more maintainable and testable.Also applies to: 41-41
99-110
: Consider caching physics initialization stateThe
_engine._physicsInitialized
check on each pointer creation could lead to inconsistent behavior if physics initialization state changes during runtime. Consider caching this state during PointerManager initialization.constructor(engine: Engine, target: EventTarget) { // ... existing code ... + this._physicsEnabled = engine._physicsInitialized; } // Later in the code -_engine._physicsInitialized && pointer._addEmitters(PointerPhysicsEventEmitter, _eventPool); +this._physicsEnabled && pointer._addEmitters(PointerPhysicsEventEmitter, _eventPool);
224-255
: Consider using event type constantsThe string literals for event types could be replaced with constants to prevent typos and improve maintainability. Consider creating an enum or constant object for event types.
const PointerEventTypes = { DOWN: 'pointerdown', UP: 'pointerup', MOVE: 'pointermove', LEAVE: 'pointerleave', CANCEL: 'pointercancel' } as const;
282-288
: Optimize cleanup operationsConsider combining the array clearing operations for better performance. You could create a helper method to clear multiple arrays at once.
private _clearArrays() { [this._nativeEvents, this._pointers, this._downList, this._downMap, this._upList, this._upMap].forEach(arr => arr.length = 0); }
41-41
: Document object pool configurationConsider adding JSDoc comments to explain the object pool's configuration and lifecycle, particularly its garbage collection strategy and pool size management.
packages/core/src/ui/UIImage.ts (2)
196-216
: Optimize property access and null checks.Multiple property accesses and null checks can be optimized by destructuring and combining conditions.
protected override _render(context: RenderContext): void { - const { _sprite: sprite } = this; - const transform = this._transform as UITransform; - const { x: width, y: height } = transform.size; - if (!sprite?.texture || !width || !height) { + const { _sprite: sprite, _transform: transform, _color: color, _alpha: alpha } = this; + if (!sprite?.texture || !transform) { return; } + const { x: width, y: height } = (transform as UITransform).size; + if (!width || !height || color.a * alpha <= 0) { + return; + } let material = this.getMaterial(); if (!material) { return; } // @todo: This question needs to be raised rather than hidden. if (material.destroyed) { material = this._engine._basicResources.uiDefaultMaterial; } - if (this._color.a * this._alpha <= 0) { - return; - }
362-371
: Consider using TypeScript const enum for better performance.The
ImageUpdateFlags
enum would benefit from being a const enum as it's used internally for bitwise operations.-enum ImageUpdateFlags { +const enum ImageUpdateFlags { UV = 0x20, /** LocalPosition | WorldPosition | UV */ AllPositionAndUV = 0x23, /** LocalPosition | WorldPosition | UV | Color */ AllPositionUVAndColor = 0x33, /** LocalPosition | WorldPosition | UV | Color | LocalBounds | WorldBounds */ All = 0x3f }packages/core/src/ui/UIText.ts (3)
235-241
: Consider adding error handling in the constructor.The constructor assumes
engine._textDefaultFont
andengine._basicResources.textDefaultMaterial
will always exist. Consider adding null checks and throwing appropriate errors if these dependencies are missing.constructor(entity: Entity) { super(entity); const { engine } = this; + if (!engine._textDefaultFont) { + throw new Error("Default font not initialized in engine."); + } this.font = engine._textDefaultFont; + if (!engine._basicResources?.textDefaultMaterial) { + throw new Error("Default text material not initialized in engine resources."); + } this.setMaterial(engine._basicResources.textDefaultMaterial); }
257-257
: Improve readability by avoiding assignment in expression.The assignment within the logical AND expression makes the code harder to read.
- this._subFont && (this._subFont = null); + if (this._subFont) { + this._subFont = null; + }🧰 Tools
🪛 Biome
[error] 257-257: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
324-363
: Consider performance optimizations in render loop.The render method performs several operations that could be optimized:
- Multiple property accesses could be cached
- Array iterations could be optimized
- Object allocations in render loop should be minimized
Consider these optimizations:
- Cache frequently accessed properties outside the loop
- Use pre-allocated arrays for temporary calculations
- Consider implementing object pooling for SubRenderElements
- Profile the rendering performance with large text content to identify bottlenecks
Would you like me to provide a detailed implementation of these optimizations?
packages/core/src/Entity.ts (1)
108-113
: Consider adding type safety for transform access.While the refactoring from public property to private property with getter is good for encapsulation, consider adding runtime type checking to prevent potential issues with transform types.
get transform(): Transform { + if (!this._transform) { + throw new Error('Transform component is missing'); + } return this._transform; }packages/core/src/ui/interactive/transition/Transition.ts (2)
6-6
: Avoid using 'any' as default type for generics to maintain type safetyThe generic type parameters
T
andK
default toany
, which reduces the benefits of TypeScript's type checking. By specifying more precise default types or removing the defaults, you can enhance type safety and catch potential errors at compile time.
114-119
: Ensure_countDown
does not become negative in_onUpdate
In the
_onUpdate()
method,_countDown
is decremented bydelta
, but there's no check to prevent it from becoming negative. Negative_countDown
values could cause unintended behavior in the transition logic.Consider clamping
_countDown
to zero after decrementing to prevent negative values.Suggested change:
if (this._countDown > 0) { - this._countDown -= delta; + this._countDown = Math.max(0, this._countDown - delta); this._updateValue(); }packages/core/src/ui/interactive/UIInteractive.ts (1)
115-115
: Separate assignments for better readabilityAssigning multiple variables in a single statement can reduce readability. Consider splitting the assignments to improve clarity.
Apply this diff:
- this._isPointerInside = this._isPointerDown = false; + this._isPointerInside = false; + this._isPointerDown = false;packages/core/src/ui/UICanvas.ts (5)
89-89
: Useif
statement instead of logical operators for clarityUsing logical operators like
&&
for control flow can reduce code readability. Consider using an explicitif
statement to make the code clearer.Apply this diff to enhance readability:
- (referenceResolution.x !== val.x || referenceResolution.y !== val.y) && referenceResolution.copyFrom(val); + if (referenceResolution.x !== val.x || referenceResolution.y !== val.y) { + referenceResolution.copyFrom(val); + }
142-143
: Useif
statement instead of logical operators for side effectsUsing logical operators like
&&
to trigger side effects can make the code harder to read and understand. Replace it with anif
statement for better clarity.Apply this diff to improve code clarity:
- this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay && - this.scene._componentsManager._overlayCanvasesSortingFlag; + if (this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay) { + this.scene._componentsManager._overlayCanvasesSortingFlag = true; + }
154-154
: Useif
statement instead of logical operators for function callsUsing logical operators like
&&
to conditionally call functions can reduce code readability. Consider using an explicitif
statement for better clarity.Apply this diff to enhance readability:
- this._realRenderMode === CanvasRenderMode.ScreenSpaceCamera && this._adapterPoseInScreenSpace(); + if (this._realRenderMode === CanvasRenderMode.ScreenSpaceCamera) { + this._adapterPoseInScreenSpace(); + }
194-194
: Avoid assignment within an expression for better readabilityAssigning values within expressions can be confusing and reduce code clarity. Separate the assignment from the declaration to improve readability.
Apply this diff to enhance code clarity:
- const renderElement = (this._renderElement = engine._renderElementPool.get()); + this._renderElement = engine._renderElementPool.get(); + const renderElement = this._renderElement;🧰 Tools
🪛 Biome
[error] 194-194: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
359-359
: Avoid assignment within a logical expressionAssigning a value within a logical expression can reduce code readability. Use an explicit
if
statement instead.Apply this diff to improve clarity:
- child.isActive && (depth = this._walk(child, elements, depth)); + if (child.isActive) { + depth = this._walk(child, elements, depth); + }🧰 Tools
🪛 Biome
[error] 359-359: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (22)
packages/core/src/Entity.ts
(11 hunks)packages/core/src/input/pointer/PointerManager.ts
(6 hunks)packages/core/src/input/pointer/emitter/PointerPhysicsEventEmitter.ts
(1 hunks)packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
(1 hunks)packages/core/src/ui/Button.ts
(1 hunks)packages/core/src/ui/UICanvas.ts
(1 hunks)packages/core/src/ui/UIGroup.ts
(1 hunks)packages/core/src/ui/UIImage.ts
(1 hunks)packages/core/src/ui/UIRenderer.ts
(1 hunks)packages/core/src/ui/UIText.ts
(1 hunks)packages/core/src/ui/UIUtils.ts
(1 hunks)packages/core/src/ui/index.ts
(1 hunks)packages/core/src/ui/interactive/InteractiveState.ts
(1 hunks)packages/core/src/ui/interactive/UIInteractive.ts
(1 hunks)packages/core/src/ui/interactive/transition/ColorTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/ScaleTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/SpriteTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/Transition.ts
(1 hunks)packages/core/src/ui/interactive/transition/TransitionType.ts
(1 hunks)packages/core/src/ui/interface/ICanvasElement.ts
(1 hunks)packages/core/src/ui/interface/IGroupElement.ts
(1 hunks)packages/core/src/ui/interface/IUIGraphics.ts
(1 hunks)
🧰 Additional context used
🪛 Biome
packages/core/src/Entity.ts
[error] 616-616: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PointerPhysicsEventEmitter.ts
[error] 67-67: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 67-67: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 223-223: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/Button.ts
[error] 21-21: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 34-34: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UICanvas.ts
[error] 194-194: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 359-359: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 473-474: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 483-484: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 485-487: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts
[error] 105-105: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 110-110: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 153-153: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts
[error] 163-163: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 167-167: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 168-168: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 166-170: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIText.ts
[error] 257-257: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 496-496: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 497-497: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 507-507: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 509-509: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 591-591: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 593-593: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts
[error] 16-153: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 132-132: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 132-132: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
packages/core/src/ui/interactive/UIInteractive.ts
[error] 147-149: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
🔇 Additional comments (35)
packages/core/src/ui/interactive/transition/TransitionType.ts (1)
1-6
: LGTM! Clean and well-structured enum implementation.
The enum provides a clear and type-safe way to represent different transition types for UI elements.
packages/core/src/ui/interface/IGroupElement.ts (1)
1-1
: Verify the GroupModifyFlags type definition.
The GroupModifyFlags
import is used but its type definition isn't visible in the current context.
✅ Verification successful
GroupModifyFlags is properly defined and used correctly
The GroupModifyFlags
enum is defined in packages/core/src/ui/UIGroup.ts
with clear flag values:
None = 0x0
Alpha = 0x1
Interactive = 0x2
All = 0x3
The import in IGroupElement.ts
correctly references this enum which is used throughout the UI system for group modifications.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the definition of GroupModifyFlags
# Expected: Find the type definition and its possible values
# Search for GroupModifyFlags definition
echo "Searching for GroupModifyFlags definition:"
ast-grep --pattern 'enum GroupModifyFlags {
$$$
}'
# Backup search using ripgrep
echo -e "\nBackup search for GroupModifyFlags:"
rg -A 5 "GroupModifyFlags"
Length of output: 7403
packages/core/src/ui/interface/ICanvasElement.ts (1)
1-2
: LGTM! Clean and focused imports.
The imports are well-organized and specifically import only the required types.
packages/core/src/ui/interface/IUIGraphics.ts (1)
1-3
: LGTM! Well-organized imports.
The imports are properly structured with clear separation between external package and internal module imports.
packages/core/src/ui/interactive/transition/SpriteTransition.ts (3)
1-5
: LGTM! Clean and well-structured class declaration.
The imports are appropriate, and the class extension with generic types is well-defined.
10-12
: Verify if binary transition is the intended behavior.
The current implementation provides an abrupt switch between source and destination sprites at weight = 1, rather than a smooth transition. If this is intentional, consider adding a comment explaining why interpolation is not used.
✅ Verification successful
Binary transition for sprites is consistent with the codebase design
The implementation is correct as sprites cannot be interpolated like other properties. This is evident from the codebase where:
- ColorTransition uses
Color.lerp
for smooth color transitions - ScaleTransition uses linear interpolation for smooth scaling
- SpriteTransition uses binary switching, which is appropriate as sprites are discrete assets
Consider adding a comment explaining this design choice to improve code clarity.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other transition implementations to verify if this binary approach is consistent
ast-grep --pattern 'class $_Transition extends Transition<$_, $_> {
$$$
protected override _updateCurrentValue($_, $_, weight: number): void {
$$$
}
$$$
}'
Length of output: 169
Script:
#!/bin/bash
# Let's try a different approach to find transition implementations
rg "_updateCurrentValue.*weight.*number" -A 5
# Also search for the base Transition class to understand the expected behavior
rg "class Transition[^a-zA-Z]" -A 10
Length of output: 3776
14-16
: Add type safety and error handling for _normal property.
The method uses this._normal
as a fallback, but this property's initialization is not visible in the current context.
Consider adding:
- Type guard to ensure value is a valid Sprite
- Explicit initialization of _normal in the constructor
- Error handling for invalid sprite values
protected override _applyValue(value: Sprite): void {
+ if (value && !this.isValidSprite(value)) {
+ throw new Error('Invalid sprite value provided');
+ }
this._target.sprite = value || this._normal;
}
+
+ private isValidSprite(value: any): value is Sprite {
+ return value && typeof value === 'object';
+ // Add additional sprite validation logic
+ }
packages/core/src/ui/index.ts (1)
1-15
: Verify all exports are properly implemented.
Let's ensure all exported components exist and are properly implemented.
✅ Verification successful
Let me generate another script to verify the specific exports since the previous output didn't show us the actual export declarations.
Based on the verification results, I can now provide the final response:
All exports are properly implemented and available
All exported components from packages/core/src/ui/index.ts
are verified to exist and are properly implemented:
- Enums:
CanvasRenderMode
andResolutionAdaptationStrategy
are properly exported - UI Components:
UIGroup
,Button
,UICanvas
,UIImage
,UIText
, andUITransform
are all exported as classes - Transitions:
ColorTransition
,ScaleTransition
,SpriteTransition
, and baseTransition
are all exported as classes
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify that all exported files exist and export the expected components
# Test: Check if all imported files exist
echo "Checking if all files exist..."
for file in "UIGroup" "enums/CanvasRenderMode" "enums/ResolutionAdaptationStrategy" \
"Button" "UICanvas" "UIImage" "UIRenderer" "UIText" "UITransform" \
"interactive/transition/ColorTransition" "interactive/transition/ScaleTransition" \
"interactive/transition/SpriteTransition" "interactive/transition/Transition"; do
if ! fd -p "packages/core/src/ui/$file.ts"; then
echo "Missing file: $file"
fi
done
# Test: Verify exports in each file
echo "Checking exports in each file..."
for file in $(fd -e ts . "packages/core/src/ui"); do
echo "Checking $file..."
ast-grep --pattern 'export class $name' "$file"
ast-grep --pattern 'export enum $name' "$file"
ast-grep --pattern 'export interface $name' "$file"
done
Length of output: 14751
Script:
#!/bin/bash
# Check specific exports in each file
# Check UIGroup export
echo "Checking UIGroup..."
rg "export (class|enum|interface) UIGroup" packages/core/src/ui/UIGroup.ts -A 1
echo "Checking enums..."
rg "export enum (CanvasRenderMode|ResolutionAdaptationStrategy)" packages/core/src/ui/enums/ -A 1
echo "Checking UI components..."
for component in "Button" "UICanvas" "UIImage" "UIRenderer" "UIText" "UITransform"; do
echo "Checking $component..."
rg "export (class|interface) $component" "packages/core/src/ui/$component.ts" -A 1
done
echo "Checking transitions..."
for transition in "ColorTransition" "ScaleTransition" "SpriteTransition" "Transition"; do
echo "Checking $transition..."
rg "export (class|interface) $transition" "packages/core/src/ui/interactive/transition/$transition.ts" -A 1
done
Length of output: 3977
packages/core/src/ui/interactive/transition/ScaleTransition.ts (2)
1-4
: LGTM! Clean and type-safe class declaration.
The imports are minimal and the generic type parameters are well-defined.
14-16
: Verify uniform scaling assumption.
The method assumes uniform scaling by only checking the x-axis scale. This might lead to unexpected behavior if the entity has non-uniform scaling.
packages/core/src/ui/interactive/transition/ColorTransition.ts (1)
1-5
: LGTM! Clean imports and proper class declaration.
The class structure and type parameters are well-defined, following TypeScript best practices.
packages/core/src/ui/Button.ts (2)
1-5
: LGTM! Clean imports and proper inheritance.
The imports are focused and the inheritance hierarchy is appropriate for a button component.
24-30
: LGTM! Efficient and safe event dispatch implementation.
The implementation correctly handles listener iteration and destroyed state checking.
packages/core/src/input/pointer/emitter/PointerPhysicsEventEmitter.ts (5)
1-11
: LGTM! Clean class structure with well-defined properties.
The class is well-organized with appropriate imports and clearly named protected properties for tracking entity states.
54-61
: LGTM! Clean and focused drag event handling.
The method is well-structured with proper null checks and clear event handling logic.
66-74
: Multiple assignments in expression reduce readability.
This issue was previously identified. The suggestion to separate the assignments for better readability is still valid.
🧰 Tools
🪛 Biome
[error] 67-67: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 67-67: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
117-119
: LGTM! Thorough cleanup of entity references.
The method properly cleans up all entity references in a single statement.
121-142
: LGTM! Well-designed helper methods.
The private methods are focused, properly encapsulated, and maintain consistent event handling patterns.
packages/core/src/ui/UIGroup.ts (4)
1-41
: LGTM! Well-structured class definition with proper property management.
The class structure follows best practices with:
- Clear separation of concerns using decorators
- Proper type definitions
- Well-documented internal properties
43-75
: LGTM! Well-implemented property accessors.
The getters and setters properly handle state updates and trigger necessary modifications.
117-144
: LGTM! Thorough lifecycle management.
The methods properly handle:
- Registration and cleanup of entity listeners
- Memory management with array clearing
- Garbage collection
186-191
: LGTM! Well-defined bit flags enum.
The enum correctly uses bit flags to enable efficient combination of multiple modification types.
packages/core/src/ui/UIRenderer.ts (1)
205-227
: 🛠️ Refactor suggestion
Optimize raycast performance and improve readability.
The raycast method can be optimized by:
- Caching the plane intersection result
- Using early returns for better readability
Apply these changes:
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean {
const plane = UIRenderer._tempPlane;
const { transform } = this._entity;
const { worldForward, worldMatrix, worldPosition } = transform;
const normal = plane.normal.copyFrom(worldForward);
plane.distance = -Vector3.dot(normal, worldPosition);
- const curDistance = ray.intersectPlane(plane);
- if (curDistance >= 0 && curDistance < distance) {
+ const intersectionDistance = ray.intersectPlane(plane);
+ if (intersectionDistance < 0 || intersectionDistance >= distance) {
+ return false;
+ }
- const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30);
+ const hitPointWorld = ray.getPoint(intersectionDistance, UIRenderer._tempVec30);
const worldMatrixInv = UIRenderer._tempMat;
- Matrix.invert(transform.worldMatrix, worldMatrixInv);
+ Matrix.invert(worldMatrix, worldMatrixInv);
const localPosition = UIRenderer._tempVec31;
Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition);
- if (this._hitTest(localPosition)) {
- out.distance = curDistance;
- out.entity = this._entity;
- out.component = this;
- out.normal.copyFrom(normal);
- out.point.copyFrom(hitPointWorld);
- return true;
- }
+ if (!this._hitTest(localPosition)) {
+ return false;
}
- return false;
+
+ out.distance = intersectionDistance;
+ out.entity = this._entity;
+ out.component = this;
+ out.normal.copyFrom(normal);
+ out.point.copyFrom(hitPointWorld);
+ return true;
}
Likely invalid or redundant comment.
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (2)
85-85
: Remove unnecessary return statement
The method is declared as void
but contains a return null
statement.
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
14-16
: Consider potential thread safety issues with static arrays
The static arrays _path0
and _path1
are shared across all instances and could lead to race conditions if multiple pointer events are processed concurrently. Consider making these instance-level properties or implementing proper synchronization.
packages/core/src/input/pointer/PointerManager.ts (1)
73-83
: Verify pointer disposal completeness
While the pointer cleanup logic is improved, ensure that all resources associated with disposed pointers are properly cleaned up. Consider adding explicit cleanup of any event listeners or cached data.
packages/core/src/ui/UIImage.ts (1)
343-356
:
Fix incorrect assembler usage in Tiled mode.
The _getUVByLocalPosition
method incorrectly uses SlicedSpriteAssembler
for the Tiled draw mode.
case SpriteDrawMode.Tiled:
- return SlicedSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
+ return TiledSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
Likely invalid or redundant comment.
packages/core/src/Entity.ts (4)
10-17
: LGTM: New imports and properties enhance UI and state management capabilities.
The additions properly support the new UI infrastructure and state management features.
Also applies to: 86-104
338-338
: LGTM: Parent change handling is properly implemented.
The new _setParentChange
method correctly propagates changes through the transform hierarchy and dispatches modification events.
Also applies to: 607-610
531-534
: LGTM: Proper cleanup of update flag manager.
The destroy method properly cleans up the update flag manager by removing all listeners and nullifying the reference.
798-798
: LGTM: Entity modification flags are well-defined.
The new enum properly categorizes different types of entity modifications, and the dispatch in _setSiblingIndex
is correctly implemented.
Also applies to: 817-824
packages/core/src/ui/interactive/UIInteractive.ts (1)
12-196
: Overall code implementation looks solid
The UIInteractive
class is well-structured, and the interactive state management logic is sound. Methods are appropriately overridden, and the use of transitions enhances the interactivity of UI elements.
🧰 Tools
🪛 Biome
[error] 147-149: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UICanvas.ts (3)
473-474
: Previous comment on missing break
statement still applies
The missing break
statement in this switch case was identified earlier and needs to be addressed to prevent unintended fall-through.
🧰 Tools
🪛 Biome
[error] 473-474: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
483-484
: Previous comment on missing break
statement still applies
The issue of the missing break
statement in this switch case remains unresolved. Please refer to the earlier feedback.
🧰 Tools
🪛 Biome
[error] 483-484: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
485-487
: Previous comment on missing break
statement still applies
Please address the previously noted missing break
statements to prevent unintended fall-through in this switch case.
🧰 Tools
🪛 Biome
[error] 485-487: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
export interface ICanvasElement { | ||
_entity: Entity; | ||
_parents: Entity[]; | ||
_rootCanvas: UICanvas; | ||
_indexInCanvas: number; | ||
_onEntityModify(flag: EntityModifyFlags): void; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider revising the interface design to follow TypeScript best practices.
Several architectural and style concerns:
-
The underscore prefix on all members suggests they are internal implementation details, which shouldn't be part of a public interface contract. Consider:
- Moving these implementation details to an abstract class
- Or removing the underscore prefix if these are meant to be public members
-
The interface appears to expose internal state management (
_parents
,_indexInCanvas
), which could make it harder to change the implementation later. -
Missing documentation for the interface and its members. Consider adding JSDoc comments to explain:
- The purpose of the interface
- The role of each member
- The contract requirements for implementing classes
Here's a suggested refactor:
+/**
+ * Represents a UI element that can be rendered on a canvas.
+ * Provides contract for managing canvas element hierarchy and modifications.
+ */
export interface ICanvasElement {
- _entity: Entity;
- _parents: Entity[];
- _rootCanvas: UICanvas;
- _indexInCanvas: number;
- _onEntityModify(flag: EntityModifyFlags): void;
+ /** The entity associated with this canvas element */
+ entity: Entity;
+ /** The parent entities in the hierarchy */
+ parents: Entity[];
+ /** The root canvas this element belongs to */
+ rootCanvas: UICanvas;
+ /** The rendering order index within the canvas */
+ indexInCanvas: number;
+ /** Handles modifications to the associated entity */
+ onEntityModify(flag: EntityModifyFlags): void;
}
Consider:
- Breaking this into smaller interfaces (e.g., separate hierarchy management from entity management)
- Using composition over inheritance by providing utility classes for common functionality
- Reducing coupling with
Entity
andUICanvas
by introducing abstraction layers
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export interface ICanvasElement { | |
_entity: Entity; | |
_parents: Entity[]; | |
_rootCanvas: UICanvas; | |
_indexInCanvas: number; | |
_onEntityModify(flag: EntityModifyFlags): void; | |
} | |
/** | |
* Represents a UI element that can be rendered on a canvas. | |
* Provides contract for managing canvas element hierarchy and modifications. | |
*/ | |
export interface ICanvasElement { | |
/** The entity associated with this canvas element */ | |
entity: Entity; | |
/** The parent entities in the hierarchy */ | |
parents: Entity[]; | |
/** The root canvas this element belongs to */ | |
rootCanvas: UICanvas; | |
/** The rendering order index within the canvas */ | |
indexInCanvas: number; | |
/** Handles modifications to the associated entity */ | |
onEntityModify(flag: EntityModifyFlags): void; | |
} |
export interface IUIGraphics extends IGroupElement { | ||
depth: number; | ||
raycastEnable: boolean; | ||
raycastPadding: Vector4; | ||
_raycast(ray: Ray, out: HitResult, distance: number): boolean; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider revising interface design and adding documentation.
-
The
_raycast
method is prefixed with underscore suggesting it's private, which is unconventional for interface contracts. Consider:- Removing the underscore if this method should be public
- Or moving this implementation detail to the concrete class if it should be private
-
The interface would benefit from JSDoc documentation explaining:
- The purpose and valid range of the
depth
property - The meaning of each component in
raycastPadding
(left, top, right, bottom) - The return value semantics of
_raycast
- The purpose and valid range of the
Here's a suggested improvement:
+/**
+ * Interface for UI graphics elements that support raycasting and depth management.
+ */
export interface IUIGraphics extends IGroupElement {
+ /** The depth value used for z-ordering. Higher values appear in front. */
depth: number;
+ /** Whether this element should participate in raycasting operations. */
raycastEnable: boolean;
+ /** Padding values (left, top, right, bottom) to expand/contract the raycast hit area. */
raycastPadding: Vector4;
- _raycast(ray: Ray, out: HitResult, distance: number): boolean;
+ /**
+ * Performs raycasting against this UI element.
+ * @param ray The ray to test against
+ * @param out The hit result information if intersection occurs
+ * @param distance The maximum distance to test
+ * @returns true if the ray intersects with this element
+ */
+ raycast(ray: Ray, out: HitResult, distance: number): boolean;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export interface IUIGraphics extends IGroupElement { | |
depth: number; | |
raycastEnable: boolean; | |
raycastPadding: Vector4; | |
_raycast(ray: Ray, out: HitResult, distance: number): boolean; | |
} | |
/** | |
* Interface for UI graphics elements that support raycasting and depth management. | |
*/ | |
export interface IUIGraphics extends IGroupElement { | |
/** The depth value used for z-ordering. Higher values appear in front. */ | |
depth: number; | |
/** Whether this element should participate in raycasting operations. */ | |
raycastEnable: boolean; | |
/** Padding values (left, top, right, bottom) to expand/contract the raycast hit area. */ | |
raycastPadding: Vector4; | |
/** | |
* Performs raycasting against this UI element. | |
* @param ray The ray to test against | |
* @param out The hit result information if intersection occurs | |
* @param distance The maximum distance to test | |
* @returns true if the ray intersects with this element | |
*/ | |
raycast(ray: Ray, out: HitResult, distance: number): boolean; | |
} |
protected _getTargetValueCopy(): Sprite { | ||
return this._target?.sprite; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider implementing actual value copying and adding documentation.
The method name suggests it returns a copy, but it's returning a reference to the original sprite. This could lead to unintended modifications of the source sprite.
Consider:
- Adding JSDoc documentation explaining the method's purpose
- Implementing actual value copying if sprite mutation should be prevented:
+ /**
+ * Returns a copy of the target's sprite value.
+ * @returns A copy of the target sprite or undefined if target is not set
+ */
protected _getTargetValueCopy(): Sprite {
- return this._target?.sprite;
+ return this._target?.sprite ? { ...this._target.sprite } : undefined;
}
Committable suggestion skipped: line range outside the PR's diff.
protected override _applyValue(value: number): void { | ||
this._target.entity.transform.setScale(value, value, value); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add validation for scale values.
The method should validate scale values to prevent negative or zero scales which could cause rendering issues.
Suggested implementation:
protected override _applyValue(value: number): void {
+ if (value <= 0) {
+ console.warn('Scale values should be positive. Clamping to 0.01');
+ value = Math.max(0.01, value);
+ }
this._target.entity.transform.setScale(value, value, value);
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
protected override _applyValue(value: number): void { | |
this._target.entity.transform.setScale(value, value, value); | |
} | |
protected override _applyValue(value: number): void { | |
if (value <= 0) { | |
console.warn('Scale values should be positive. Clamping to 0.01'); | |
value = Math.max(0.01, value); | |
} | |
this._target.entity.transform.setScale(value, value, value); | |
} |
protected override _applyValue(value: Color): void { | ||
this._target.color = value; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add null check for target in _applyValue method.
The _applyValue method should validate that _target exists before attempting to set its color property.
Apply this fix:
protected override _applyValue(value: Color): void {
+ if (!this._target) {
+ return;
+ }
this._target.color = value;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
protected override _applyValue(value: Color): void { | |
this._target.color = value; | |
} | |
protected override _applyValue(value: Color): void { | |
if (!this._target) { | |
return; | |
} | |
this._target.color = value; | |
} |
set normal(value: T) { | ||
const preNormal = this._normal; | ||
if (preNormal !== value) { | ||
this._normal = value; | ||
this._onStateValueDirty(InteractiveState.Normal); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use deep comparison in setters to detect changes in complex objects
In the setters for normal
, pressed
, hover
, and disabled
, the comparison if (preValue !== value)
may not detect changes in complex types like Sprite
, Color
, or Vector3
since it compares object references rather than their contents. If the properties of these objects change without the reference changing, the update might not be detected.
Consider implementing a deep comparison or using an equals
method to accurately detect changes in the values.
Also applies to: 35-41, 47-53, 59-65
private _updateValue() { | ||
const weight = this._duration ? 1 - this._countDown / this._duration : 1; | ||
this._updateCurrentValue(this._initialValue, this._finalValue, weight); | ||
this._target?.enabled && this._applyValue(this._currentValue); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clamp weight
between 0 and 1 in _updateValue
to ensure valid interpolation
In the _updateValue()
method, weight
can become less than 0 or greater than 1 if _countDown
becomes negative or exceeds _duration
due to floating-point inaccuracies or unexpected usage. This can lead to invalid interpolation results.
Consider clamping weight
between 0 and 1 to maintain valid interpolation.
Suggested fix:
private _updateValue() {
- const weight = this._duration ? 1 - this._countDown / this._duration : 1;
+ const weight = this._duration ? Math.min(1, Math.max(0, 1 - this._countDown / this._duration)) : 1;
this._updateCurrentValue(this._initialValue, this._finalValue, weight);
this._target?.enabled && this._applyValue(this._currentValue);
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private _updateValue() { | |
const weight = this._duration ? 1 - this._countDown / this._duration : 1; | |
this._updateCurrentValue(this._initialValue, this._finalValue, weight); | |
this._target?.enabled && this._applyValue(this._currentValue); | |
} | |
private _updateValue() { | |
const weight = this._duration ? Math.min(1, Math.max(0, 1 - this._countDown / this._duration)) : 1; | |
this._updateCurrentValue(this._initialValue, this._finalValue, weight); | |
this._target?.enabled && this._applyValue(this._currentValue); | |
} |
set duration(value: number) { | ||
if (value < 0) value = 0; | ||
const preDuration = this._duration; | ||
if (preDuration !== value) { | ||
this._duration = value; | ||
if (this._countDown > 0) { | ||
this._countDown = value * (1 - this._countDown / preDuration); | ||
this._updateValue(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prevent division by zero when adjusting _countDown
in duration
setter
In the duration
setter, when adjusting _countDown
, there is a potential for division by zero if preDuration
is zero. This can result in NaN
or Infinity
values, causing unexpected behavior in the transition timing.
Consider adding a check to handle the scenario when preDuration
is zero to prevent division by zero.
Here's a possible fix:
if (preDuration !== value) {
this._duration = value;
if (this._countDown > 0) {
+ if (preDuration > 0) {
this._countDown = value * (1 - this._countDown / preDuration);
+ } else {
+ this._countDown = value;
+ }
this._updateValue();
}
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
set duration(value: number) { | |
if (value < 0) value = 0; | |
const preDuration = this._duration; | |
if (preDuration !== value) { | |
this._duration = value; | |
if (this._countDown > 0) { | |
this._countDown = value * (1 - this._countDown / preDuration); | |
this._updateValue(); | |
} | |
} | |
} | |
set duration(value: number) { | |
if (value < 0) value = 0; | |
const preDuration = this._duration; | |
if (preDuration !== value) { | |
this._duration = value; | |
if (this._countDown > 0) { | |
if (preDuration > 0) { | |
this._countDown = value * (1 - this._countDown / preDuration); | |
} else { | |
this._countDown = value; | |
} | |
this._updateValue(); | |
} | |
} | |
} |
case EntityModifyFlags.UICanvasEnableInScene: | ||
case EntityModifyFlags.Parent: | ||
UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity)); | ||
UIUtils.registerEntityListener(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing break
statements to prevent unintended fallthrough in switch cases
The switch cases for EntityModifyFlags.UICanvasEnableInScene
and EntityModifyFlags.Parent
lack break
statements, causing unintended fallthrough to the next case. This could lead to unexpected behavior.
Apply this diff to add the missing break
statements:
145 switch (flag) {
146 case EntityModifyFlags.UICanvasEnableInScene:
147 case EntityModifyFlags.Parent:
148 UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity));
149 UIUtils.registerEntityListener(this);
+150 break;
151 case EntityModifyFlags.UIGroupEnableInScene:
152 UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity));
153 break;
154 default:
155 break;
156 }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
case EntityModifyFlags.UICanvasEnableInScene: | |
case EntityModifyFlags.Parent: | |
UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity)); | |
UIUtils.registerEntityListener(this); | |
case EntityModifyFlags.UICanvasEnableInScene: | |
case EntityModifyFlags.Parent: | |
UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity)); | |
UIUtils.registerEntityListener(this); | |
break; |
🧰 Tools
🪛 Biome
[error] 147-149: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UICanvas.ts
Outdated
const { min, max } = renderer.bounds; | ||
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { | ||
continue; | ||
} | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrap variable declarations in switch cases within blocks
Variables declared within switch cases can be accessed by other cases if not properly scoped. To prevent unintended access and improve code safety, wrap the case block in braces {}
.
Apply this diff to scope the variable declaration:
case CanvasRenderMode.ScreenSpaceOverlay:
+ {
const { min, max } = renderer.bounds;
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) {
continue;
}
break;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const { min, max } = renderer.bounds; | |
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { | |
continue; | |
} | |
break; | |
{ | |
const { min, max } = renderer.bounds; | |
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { | |
continue; | |
} | |
break; | |
} |
🧰 Tools
🪛 Biome
[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Outside diff range and nitpick comments (27)
packages/core/src/ui/interface/IUIGroupable.ts (2)
8-8
: Add method documentation and explicit return type.The method signature could be improved with the following:
- Add JSDoc documentation explaining when this method is called and its purpose
- Explicitly specify the return type for better type safety
Here's the suggested improvement:
+ /** + * Called when the group this element belongs to is modified. + * @param flag - Indicates the type of modification made to the group + */ - _onGroupModify(flag: GroupModifyFlags): void; + _onGroupModify(flag: GroupModifyFlags): void;
4-9
: Consider providing usage examples and integration guidelines.As this interface is part of the UI infrastructure, consider:
- Adding examples in the documentation showing how to implement this interface
- Creating integration tests to verify group behavior
- Providing guidelines for when to use this interface vs. direct UI elements
This will help other developers understand when and how to use groupable UI elements effectively.
packages/core/src/ui/interface/IUIElement.ts (1)
4-11
: Add JSDoc documentation and readonly modifiers.The interface would benefit from:
- JSDoc documentation explaining its purpose and member usage
- Readonly modifiers for references that shouldn't be mutated directly
+/** + * Represents a UI element that can be rendered in a canvas hierarchy. + * Provides core functionality for UI element management and modification tracking. + */ export interface IUIElement { + /** The entity associated with this UI element */ - _entity: Entity; + readonly _entity: Entity; + /** Array of parent entities in the hierarchy */ - _parents: Entity[]; + readonly _parents: Entity[]; + /** The root canvas containing this UI element */ - _rootCanvas: UICanvas; + readonly _rootCanvas: UICanvas; + /** The index position of this element within its canvas */ _indexInCanvas: number; + /** + * Handles modifications to the associated entity + * @param flag The type of entity modification + */ _onEntityModify(flag: EntityModifyFlags): void; }packages/core/src/ui/index.ts (2)
5-11
: LGTM! UI components hierarchy is well-structured.The exports follow a logical progression from infrastructure (Canvas, Renderer, Transform) to widgets (Button, Image, Text).
Consider documenting the recommended usage patterns and component hierarchy in a README.md file to help developers understand how these components should be composed together.
1-15
: Consider improving file organization with grouping comments.While the exports are logically grouped, adding separator comments would improve readability.
Consider applying this organization:
+// Core export { UIGroup } from "./UIGroup"; + +// Enums export { CanvasRenderMode } from "./enums/CanvasRenderMode"; export { ResolutionAdaptationStrategy } from "./enums/ResolutionAdaptationStrategy"; +// Infrastructure export { UICanvas } from "./UICanvas"; export { UIRenderer } from "./UIRenderer"; export { UITransform } from "./UITransform"; + +// Widgets export { Button } from "./Button"; export { Image } from "./Image"; export { Text } from "./Text"; +// Transitions export { ColorTransition } from "./interactive/transition/ColorTransition"; export { ScaleTransition } from "./interactive/transition/ScaleTransition"; export { SpriteTransition } from "./interactive/transition/SpriteTransition"; export { Transition } from "./interactive/transition/Transition";packages/core/src/ui/UIUtils.ts (3)
21-37
: Add null check for element parameterThe method assumes
element
is not null but doesn't validate it. Consider adding a guard clause to handle invalid input.static registerEntityListener(element: IUIElement): void { + if (!element) { + return; + } const parents = element._parents;
84-126
: Improve type safety with type guardsReplace type casting with type guard functions to make the code more type-safe and maintainable.
+private static isUICanvas(component: any): component is UICanvas { + return component._componentType === ComponentType.UICanvas; +} + +private static isUIGroup(component: any): component is UIGroup { + return component._componentType === ComponentType.UIGroup; +} static getRootCanvasInParent(entity: Entity): UICanvas { while (entity) { const components = entity._components; for (let i = 0, n = components.length; i < n; i++) { const component = components[i]; - if ( - component.enabled && - component._componentType === ComponentType.UICanvas && - (<UICanvas>component)._isRootCanvas - ) { - return <UICanvas>component; + if (component.enabled && this.isUICanvas(component) && component._isRootCanvas) { + return component; } }
136-136
: Consider using Matrix methods instead of direct element mutationDirect mutation of matrix elements makes the code harder to maintain and understand. Consider using Matrix class methods.
-projectE[0] = 2 / canvas.width; -projectE[5] = 2 / canvas.height; -projectE[10] = 0; +virtualCamera.projectionMatrix.setOrtho( + 0, canvas.width, + 0, canvas.height, + -1, 1 +);packages/core/src/ui/UIGroup.ts (3)
9-41
: Add class-level documentationConsider adding comprehensive JSDoc documentation for the
UIGroup
class to describe its purpose, responsibilities, and usage patterns.Add documentation like this:
/** * Manages a group of UI elements with hierarchical properties like alpha and interactivity. * Handles propagation of modifications to child elements and groups. * * @example * ```typescript * const uiGroup = new UIGroup(entity); * uiGroup.alpha = 0.5; // Sets opacity for all child elements * ``` */ export class UIGroup extends Component {
123-144
: Consider decomposing _onDisableInScene for better maintainabilityThe method handles multiple concerns: entity listener cleanup, element cleanup, and group cleanup. Consider breaking it down into smaller, focused methods.
private _cleanupEntityListeners(): void { const entityListeners = this._entityListeners; entityListeners.forEach(entity => entity._unRegisterModifyListener(this._onEntityModify) ); entityListeners.length = 0; } private _cleanupElements(): void { const disorderedElements = this._disorderedElements; disorderedElements.forEach(element => UIUtils.registerElementToGroup(element, this._parentGroup) ); disorderedElements.length = 0; disorderedElements.garbageCollection(); } private _cleanupGroups(): void { const disorderedGroups = this._disorderedGroups; disorderedGroups.forEach(element => element._registryToParentGroup(this._parentGroup) ); disorderedGroups.length = 0; disorderedGroups.garbageCollection(); } override _onDisableInScene(): void { this._cleanupEntityListeners(); this._cleanupElements(); this._cleanupGroups(); this._parentGroup = null; this._entity._dispatchModify(EntityModifyFlags.UIGroupDisableInScene); }
186-191
: Add documentation for GroupModifyFlagsConsider adding JSDoc comments to document the purpose and usage of each flag.
/** * Flags indicating which aspects of a UI group need to be modified. */ export enum GroupModifyFlags { /** No modifications needed */ None = 0x0, /** Alpha (opacity) value needs to be updated */ Alpha = 0x1, /** Interactive state needs to be updated */ Interactive = 0x2, /** All properties need to be updated */ All = 0x3 }packages/core/src/ui/UIRenderer.ts (1)
22-28
: Consider implementing object pooling for temporary objects.Using static temporary objects could lead to memory allocation overhead during frequent UI updates. Consider implementing an object pool to manage these temporary objects more efficiently.
class UIRendererObjectPool { private static _vec3Pool: Vector3[] = []; private static _matrixPool: Matrix[] = []; static acquireVector3(): Vector3 { return this._vec3Pool.pop() || new Vector3(); } static releaseVector3(vec: Vector3): void { vec.set(0, 0, 0); this._vec3Pool.push(vec); } }packages/core/src/ui/UICanvas.ts (2)
20-24
: Consider type inference for numeric and boolean properties.TypeScript can automatically infer types from initial values. You can simplify the property declarations by removing explicit type annotations where the type is obvious from the initialization.
Apply this diff to simplify the type declarations:
- _isRootCanvas: boolean = false; + _isRootCanvas = false; - _canvasIndex: number = -1; + _canvasIndex = -1; - _sortDistance: number = 0; + _sortDistance = 0; - private _sortOrder: number = 0; + private _sortOrder = 0; - private _distance: number = 10; + private _distance = 10; - private _referenceResolution: Vector2 = new Vector2(800, 600); + private _referenceResolution = new Vector2(800, 600);Also applies to: 27-27, 45-45, 64-64, 66-66, 70-70
284-284
: Rename methods to use correct terminology.The methods use "adapter" instead of "adapt" in their names, which is grammatically incorrect.
Apply this diff to fix the method names:
- private _adapterPoseInScreenSpace(): void { + private _adaptPoseInScreenSpace(): void { - private _adapterSizeInScreenSpace(): void { + private _adaptSizeInScreenSpace(): void {Update all references to these methods accordingly.
Also applies to: 303-303
packages/core/src/2d/text/TextRenderer.ts (2)
Line range hint
87-611
: Consider batching dirty flag updates.The current implementation sets dirty flags individually for each property change. Consider implementing a batch update mechanism for scenarios where multiple properties are changed simultaneously.
+ private _batchUpdate(callback: () => void): void { + const prevDirtyFlag = this._dirtyUpdateFlag; + callback(); + if (this._dirtyUpdateFlag !== prevDirtyFlag) { + this._onDirtyFlagChanged(this._dirtyUpdateFlag); + } + } set text(value: string) { value = value || ""; if (this._text !== value) { + this._batchUpdate(() => { this._text = value; this._setDirtyFlagTrue(RendererUpdateFlags.AllPositionAndBounds); + }); } }
689-699
: Add JSDoc comments for individual flags.The
TextRendererUpdateFlags
enum would benefit from detailed documentation for each flag, explaining when they are set and their implications.enum TextRendererUpdateFlags { + /** Flag indicating that the SubFont needs to be updated */ SubFont = 0x10, + /** Flag indicating that the Color needs to be updated */ Color = 0x20, + /** + * Combined flag for font-related updates that affect position and bounds + * @remarks This includes SubFont and all position/bounds flags + */ FontAllPositionAndBounds = 0x1f, + /** + * Combined flag for all possible updates + * @remarks This includes all font, position, bounds, and color flags + */ All = 0x3f }packages/core/src/Entity.ts (1)
404-404
: Consider adding a type check for UITransform inheritance.The current implementation assumes
UITransform
is a direct class, but it might be inherited.- this._transform instanceof UITransform && child.addComponent(UITransform); + (this._transform instanceof UITransform || this._transform.constructor === UITransform) && child.addComponent(UITransform);packages/core/src/ui/interactive/transition/Transition.ts (3)
1-155
: Consider adding astop
method to halt the transition.It might be useful to have a way to immediately stop the transition and set the value to the final state. This can be helpful in scenarios where you want to cancel the transition midway and snap to the end state.
Here's a possible implementation:
stop() { this._countDown = 0; this._initialValue = this._finalValue = this._getValueByState(this._finalState); this._updateValue(); }
1-155
: Consider adding anonTransitionEnd
callback.It could be beneficial to provide a way for users to register a callback that gets invoked when the transition reaches its end state. This can be useful for triggering additional actions or state changes based on the completion of the transition.
Here's a possible implementation:
private _onTransitionEnd: () => void; set onTransitionEnd(callback: () => void) { this._onTransitionEnd = callback; } private _updateValue() { // ... if (this._countDown <= 0 && this._onTransitionEnd) { this._onTransitionEnd(); } }
1-155
: Consider adding support for easing functions.Currently, the transition uses linear interpolation between the initial and final values. Introducing support for easing functions can provide more control over the transition's behavior and allow for more visually appealing animations.
Here's a possible implementation:
type EasingFunction = (t: number) => number; private _easingFunction: EasingFunction = (t) => t; set easingFunction(fn: EasingFunction) { this._easingFunction = fn; } private _updateValue() { const t = this._duration ? 1 - this._countDown / this._duration : 1; const weight = this._easingFunction(t); // ... }You can then provide a set of built-in easing functions or allow users to define their own.
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (1)
229-231
: Consider defining the magic number as a constant for better maintainability.The magic number
1024
is used to prevent infinite loops by limiting the iteration count. Defining this number as a constant with a descriptive name would enhance code readability and make future adjustments easier.Apply this diff to define the magic number as a constant:
- for (; i < 1024 && !!entity && entity !== rootEntity; i++) { + const MAX_PATH_DEPTH = 1024; + for (; i < MAX_PATH_DEPTH && !!entity && entity !== rootEntity; i++) { entity = path[i] = entity.parent; }packages/core/src/ui/Image.ts (1)
208-209
: Address the TODO comment regarding material handlingThere's a TODO comment indicating that an issue needs to be raised rather than hidden when the material is destroyed. This should be resolved to ensure proper error handling and developer awareness.
Do you want assistance in implementing the error handling mechanism or opening a GitHub issue to track this task?
packages/core/src/ui/Text.ts (5)
225-225
: Avoid assignments within expressions.The static analysis tool has flagged several instances where assignments are used within expressions. While this is syntactically valid, it can lead to confusion and potential bugs.
Consider separating the assignments from the expressions to improve readability and maintainability. For example:
- firstRow < 0 && (firstRow = j); + if (firstRow < 0) { + firstRow = j; + }This applies to the assignments on lines 225, 466, 467, 477, 479, 565, and 567.
Also applies to: 466-466, 467-467, 477-477, 479-479, 565-565, 567-567
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
293-331
: Consider extracting the rendering logic into a separate method.The
_render
method is quite lengthy and contains multiple responsibilities, such as updating positions, colors, and managing render elements. To improve readability and maintainability, consider extracting some of the rendering logic into separate methods.For example, you could extract the code that creates and sets up the
SubRenderElement
instances into a separate method, like_createSubRenderElements
. This would make the_render
method more focused and easier to understand.
402-541
: Optimize the_updateLocalData
method.The
_updateLocalData
method is a critical part of the text rendering process, and its performance can impact the overall rendering performance. Consider the following optimizations:
- Preallocate and reuse temporary arrays and objects to reduce memory allocations.
- Use object pooling for frequently created objects like
CharRenderInfo
andTextChunk
.- Minimize the number of iterations and loops where possible.
- Use more efficient data structures, such as typed arrays, for performance-critical operations.
These optimizations can help improve the performance of the
_updateLocalData
method, especially when dealing with large amounts of text.🧰 Tools
🪛 Biome
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
561-590
: Use a profiler to analyze the performance of the_buildChunk
method.The
_buildChunk
method is responsible for building the vertex and index data for each text chunk. It's important to ensure that this method is performant, as it can be called frequently during text rendering.Consider using a profiler to analyze the performance of this method and identify any potential bottlenecks. Pay attention to the following:
- Memory allocations and deallocations.
- Iteration and loop performance.
- Data structure access and manipulation.
Based on the profiling results, optimize the method as needed to improve its performance. This may involve techniques such as object pooling, preallocating arrays, or using more efficient data structures.
🧰 Tools
🪛 Biome
[error] 565-565: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 567-567: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
611-615
: Consider using a more memory-efficient data structure forTextChunk
.The
TextChunk
class currently uses an array to storeCharRenderInfo
instances. Depending on the size of the text and the number of chunks, this can lead to significant memory usage.Consider using a more memory-efficient data structure, such as a linked list or a custom allocator, to store the
CharRenderInfo
instances. This can help reduce memory overhead, especially when dealing with large amounts of text.Additionally, explore the possibility of reusing
TextChunk
instances through object pooling to further optimize memory usage.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (17)
packages/core/src/2d/text/TextRenderer.ts
(20 hunks)packages/core/src/2d/text/TextUtils.ts
(6 hunks)packages/core/src/Entity.ts
(10 hunks)packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
(1 hunks)packages/core/src/ui/Image.ts
(1 hunks)packages/core/src/ui/Text.ts
(1 hunks)packages/core/src/ui/UICanvas.ts
(1 hunks)packages/core/src/ui/UIGroup.ts
(1 hunks)packages/core/src/ui/UIRenderer.ts
(1 hunks)packages/core/src/ui/UIUtils.ts
(1 hunks)packages/core/src/ui/index.ts
(1 hunks)packages/core/src/ui/interactive/UIInteractive.ts
(1 hunks)packages/core/src/ui/interactive/transition/SpriteTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/Transition.ts
(1 hunks)packages/core/src/ui/interface/IUIElement.ts
(1 hunks)packages/core/src/ui/interface/IUIGraphics.ts
(1 hunks)packages/core/src/ui/interface/IUIGroupable.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/core/src/2d/text/TextUtils.ts
- packages/core/src/ui/interactive/transition/SpriteTransition.ts
- packages/core/src/ui/interface/IUIGraphics.ts
🧰 Additional context used
🪛 Biome
packages/core/src/Entity.ts
[error] 612-612: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/Text.ts
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 565-565: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 567-567: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UICanvas.ts
[error] 194-194: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 357-357: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 471-472: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 481-482: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 483-485: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts
[error] 105-105: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 110-110: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 153-153: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts
[error] 158-158: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 162-162: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 163-163: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 161-165: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIUtils.ts
[error] 16-153: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 132-132: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 132-132: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
packages/core/src/ui/interactive/UIInteractive.ts
[error] 132-134: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
🔇 Additional comments (44)
packages/core/src/ui/interface/IUIGroupable.ts (1)
1-2
: LGTM! Imports are well-organized.
The necessary dependencies are correctly imported using appropriate relative paths.
packages/core/src/ui/interface/IUIElement.ts (2)
1-3
: LGTM! Imports are clean and well-organized.
All necessary types are properly imported, and there are no redundant imports.
10-10
: Consider adding canvas modification handler.
The interface has _onEntityModify
but lacks a corresponding method for handling canvas modifications. This asymmetry might indicate a missing feature.
Consider adding an _onCanvasModify
method if canvas-level changes need to be tracked:
_onCanvasModify(flag: CanvasModifyFlags): void;
packages/core/src/ui/index.ts (2)
1-4
: LGTM! Core exports are well-organized.
The core UI group and enum exports provide essential building blocks for the GUI infrastructure.
12-15
: LGTM! Transition system exports are comprehensive.
The transition system appears well-thought-out with support for different transition types (Color, Scale, Sprite).
packages/core/src/ui/UIUtils.ts (1)
137-137
: Add error handling for RHI operations
The RHI operation could fail in certain scenarios. Consider adding error handling.
packages/core/src/ui/UIGroup.ts (1)
164-177
: Verify entity listener cleanup
The entity listener registration in the while loop could potentially lead to memory leaks if the entity hierarchy changes frequently. Consider adding a verification step to ensure all listeners are properly cleaned up.
✅ Verification successful
Entity listener cleanup is properly implemented
The codebase shows a robust implementation of entity listener cleanup:
- In UIGroup, listeners are properly unregistered in a loop before clearing the array:
entityListeners[i]._unRegisterModifyListener(this._onEntityModify)
- When replacing listeners in the hierarchy, old listeners are unregistered before new ones are registered:
preListener?._unRegisterModifyListener(this._onEntityModify)
- The pattern is consistently used across related components (UIUtils, UICanvas) showing a systematic approach to listener management
The original concern about potential memory leaks is unfounded as the code maintains proper cleanup of entity listeners.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for potential memory leaks in entity listener management
echo "Searching for entity listener registration patterns..."
ast-grep --pattern 'entity._registerModifyListener($_)'
echo "Searching for corresponding cleanup patterns..."
ast-grep --pattern 'entity._unRegisterModifyListener($_)'
# Look for potential memory leak patterns in test files
echo "Searching for test cases covering listener cleanup..."
rg -l "test.*UIGroup.*cleanup" -t ts
Length of output: 695
Script:
#!/bin/bash
# Let's search for unregister patterns with a broader context
echo "Searching for unregister patterns..."
rg "_unRegisterModifyListener" -A 2 -B 2
# Check for cleanup in UIGroup class destructor or dispose methods
echo "Searching for cleanup methods in UIGroup..."
ast-grep --pattern 'class UIGroup {
$$$
dispose() {
$$$
}
$$$
}'
# Check if there are any tests for UIGroup
echo "Searching for UIGroup test files..."
fd "UIGroup.*test" -e ts
Length of output: 2984
packages/core/src/ui/UIRenderer.ts (1)
19-20
:
Implement ISpriteRenderer interface.
The class should implement the ISpriteRenderer interface to maintain consistency with the sprite rendering system.
-export abstract class UIRenderer extends Renderer implements IUIGraphics {
+export abstract class UIRenderer extends Renderer implements IUIGraphics, ISpriteRenderer {
Likely invalid or redundant comment.
packages/core/src/ui/UICanvas.ts (1)
470-492
: 🛠️ Refactor suggestion
Document intentional switch case fallthrough.
The switch cases in _setRealRenderMode
are falling through intentionally to execute multiple actions, but this pattern should be explicitly documented to prevent confusion.
Apply this diff to add fallthrough comments:
switch (curRealMode) {
case CanvasRenderMode.ScreenSpaceOverlay:
this._addCanvasListener();
+ // fallthrough intentional
case CanvasRenderMode.ScreenSpaceCamera:
this._adapterPoseInScreenSpace();
this._adapterSizeInScreenSpace();
+ // fallthrough intentional
case CanvasRenderMode.WorldSpace:
componentsManager.addUICanvas(curRealMode, this);
break;
Likely invalid or redundant comment.
🧰 Tools
🪛 Biome
[error] 471-472: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 481-482: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 483-485: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/2d/text/TextRenderer.ts (2)
35-35
: Fix array initialization.
The array initialization syntax is incorrect. Using Array<TextChunk>()
without the new
keyword won't create a new array instance.
475-478
: 🛠️ Refactor suggestion
Add error handling for text measurement edge cases.
The text measurement logic should handle edge cases such as extremely large text or invalid font states.
private _updateLocalData(): void {
const { min, max } = this._localBounds;
const charRenderInfos = TextRenderer._charRenderInfos;
const charFont = this._getSubFont();
+ try {
const textMetrics = this.enableWrapping
? TextUtils.measureTextWithWrap(this, this.width, this.height)
: TextUtils.measureTextWithoutWrap(this, this.height);
+ } catch (error) {
+ console.error('Text measurement failed:', error);
+ min.set(0, 0, 0);
+ max.set(0, 0, 0);
+ return;
+ }
packages/core/src/Entity.ts (8)
99-104
: LGTM! Property changes enhance state management.
The addition of _updateFlagManager
and conversion of transform
to a private property with getter improves encapsulation.
107-110
: LGTM! Transform getter maintains backward compatibility.
The getter provides controlled access to the private _transform
property while maintaining the public API.
527-530
: LGTM! Proper cleanup of update flag manager.
The cleanup ensures no memory leaks by removing all listeners and nullifying the reference.
603-606
: LGTM! Parent change handling is comprehensive.
The method properly updates transform and dispatches modification flags.
695-695
: LGTM! Parent change notification is properly placed.
The _setParentChange
call is correctly placed after all parent-related updates.
771-771
: LGTM! Sibling index modification notification.
The dispatch of the sibling index modification flag is properly placed after the index update.
783-783
: LGTM! World matrix update uses private transform.
The update correctly uses the private _transform
property.
790-797
: LGTM! Comprehensive entity modification flags.
The enum provides a clear set of flags for tracking different types of entity modifications.
packages/core/src/ui/interactive/transition/Transition.ts (4)
26-32
: ****
Use deep comparison in setters to detect changes in complex objects.
Also applies to: 35-41, 47-53, 59-65
85-95
: ****
Prevent division by zero when adjusting _countDown
in duration
setter.
128-132
: ****
Clamp weight
between 0 and 1 in _updateValue
to ensure valid interpolation.
1-155
: LGTM!
The Transition
class provides a solid foundation for managing UI transitions. The use of generics allows flexibility in handling different value types and UI renderers. The class encapsulates the transition logic effectively, with clear separation of concerns between the abstract methods and the internal implementation.
The class follows good practices such as:
- Using getters and setters for properties to encapsulate internal state changes.
- Providing a clear API for setting the transition state and updating values.
- Handling edge cases like negative duration values and adjusting the countdown accordingly.
- Utilizing abstract methods to allow customization of transition behavior by subclasses.
The code is well-structured, readable, and maintainable. The comments provide helpful insights into the purpose and usage of the class and its methods.
Overall, the Transition
class is a valuable addition to the UI framework and lays a solid groundwork for implementing various types of transitions in a consistent and extensible manner.
packages/core/src/ui/interactive/UIInteractive.ts (7)
1-11
: LGTM!
The imports are well-organized and follow a logical order. The imported entities are relevant to the functionality of the UIInteractive
class.
12-82
: LGTM!
The UIInteractive
class is well-structured and implements the necessary properties and methods for managing interactive UI elements. The use of TypeScript features such as access modifiers, getters/setters, and type generics enhances the code's readability and maintainability.
The class provides a clean API for managing transitions and updating the interactive state based on pointer events. The onUpdate
method ensures that transitions are updated efficiently.
84-102
: LGTM!
The pointer event handling methods (onPointerBeginDrag
, onPointerEndDrag
, onPointerEnter
, onPointerExit
) are implemented correctly. They update the internal state variables and trigger state updates appropriately.
107-123
: LGTM!
The _onEnableInScene
and _onDisableInScene
methods handle the registration and unregistration of the element with the UI utilities effectively. They ensure proper integration with the scene and update the interactive state accordingly.
147-155
: LGTM!
The _onGroupModify
method correctly handles the GroupModifyFlags.Interactive
flag. It updates the runtime interactive state and triggers a state update when necessary.
157-174
: LGTM!
The private methods _updateState
and _getInteractiveState
are implemented correctly. They manage the current state of interactivity based on the runtime interactive flag and pointer interactions.
131-134
:
The previous review comment regarding the missing break
statement is still valid. Please address this issue to prevent unintended fallthrough.
Apply this diff to add the missing break
statement:
131 case EntityModifyFlags.UICanvasEnableInScene:
132 case EntityModifyFlags.Parent:
133 UIUtils.registerElementToCanvas(this, UIUtils.getRootCanvasInParent(this._entity));
134 UIUtils.registerEntityListener(this);
+135 break;
136 case EntityModifyFlags.UIGroupEnableInScene:
🧰 Tools
🪛 Biome
[error] 132-134: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (6)
85-85
: ****
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
88-89
: ****
102-102
: ****
🧰 Tools
🪛 Biome
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
167-190
: ****
222-222
: ****
🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
1-236
: The PointerUIEventEmitter
class provides a structured approach to managing pointer interactions with UI components.
The class extends the PointerEventEmitter
and overrides key methods to handle pointer events specifically for UI components. It maintains references to the currently entered, pressed, and dragged UI elements, enabling accurate event processing based on pointer interactions.
The implementation follows a clear and logical flow, with well-defined responsibilities for each method. The code is modular, making it easier to understand and maintain. The use of private helper methods for common tasks like updating the raycast, bubbling events, and composing paths further enhances the code organization.
Overall, the PointerUIEventEmitter
class is a solid addition to the pointer event handling system, providing a dedicated mechanism for managing UI component interactions.
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/Image.ts (7)
42-43
: Ensure alphaHitTestMinimumThreshold
is clamped between 0 and 1
The setter for alphaHitTestMinimumThreshold
correctly clamps the value. Just make sure that any value assignments are within the expected range.
52-71
: Update assembler initialization in drawMode
setter
The logic for updating the _assembler
based on drawMode
is appropriate. This ensures the correct assembler is used for rendering.
81-87
: Correct handling of tileMode
setter
The tileMode
setter correctly updates the dirty flags when the mode changes. This maintains the integrity of the rendering process.
113-130
: Proper management of sprite resource in sprite
setter
The setter effectively manages the reference counting and listener management for sprite resources. This helps prevent memory leaks and ensures resources are properly cleaned up.
285-298
: Resource cleanup in _onDestroy
The _onDestroy
method properly handles resource cleanup by decreasing reference counts and removing listeners. This prevents potential memory leaks.
300-341
: Comprehensive handling in _onSpriteChange
method
The _onSpriteChange
method effectively updates the dirty flags based on the type of sprite modification. This ensures the renderer reacts appropriately to sprite changes.
343-356
: Verify the UV calculation in _getUVByLocalPosition
While the method delegates UV calculation to the assembler, ensure that for SpriteDrawMode.Tiled
, the correct assembler is used. Currently, it calls SlicedSpriteAssembler.getUVByLocalPosition
for both Sliced
and Tiled
modes. Is this intentional?
Please confirm that using SlicedSpriteAssembler
for Tiled
mode is correct.
If not, consider the following change:
case SpriteDrawMode.Tiled:
- return SlicedSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
+ return TiledSpriteAssembler.getUVByLocalPosition(this, size.x, size.y, pivot, position, out);
packages/core/src/ui/Text.ts (1)
1-627
: Comprehensive and well-structured implementation of the Text class.
The Text
class provides a robust and feature-rich implementation for rendering text in a 2D UI context. It effectively manages text properties, rendering states, and efficiently handles text layout and rendering.
Key highlights:
- Utilizes dirty flags to optimize rendering and minimize unnecessary updates.
- Implements proper resource management, including reference counting for fonts and memory pooling for character render information.
- Supports various text formatting options such as alignment, wrapping, overflow modes, and font styles.
- Efficiently manages text chunks and batches rendering for optimal performance.
- Integrates with the
ISpriteRenderer
interface and other UI components.
The code follows good practices such as:
- Appropriate use of access modifiers for encapsulation.
- Meaningful naming conventions for variables, methods, and enums.
- Proper error handling and input validation.
- Efficient memory management through object pooling.
- Modular and reusable design with clear separation of concerns.
Overall, the Text
class is a solid foundation for text rendering in the UI system.
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 565-565: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 567-567: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
export interface IUIGroupable extends IUIElement { | ||
_group: UIGroup; | ||
_indexInGroup: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add JSDoc documentation and improve type safety.
The interface and its properties should be documented for better maintainability. Also, consider the following improvements:
- Add JSDoc comments explaining the purpose of the interface and its properties
- Consider using
protected
orprivate
modifiers since properties are prefixed with underscore - Add type constraints for
_indexInGroup
to ensure non-negative values
Here's the suggested improvement:
+/**
+ * Interface for UI elements that can be grouped together.
+ * Provides necessary properties and methods for group management.
+ */
export interface IUIGroupable extends IUIElement {
+ /** The group this element belongs to */
_group: UIGroup;
+ /** Zero-based index of this element within its group */
- _indexInGroup: number;
+ _indexInGroup: NonNegativeInteger;
}
+ type NonNegativeInteger = number & { __brand: 'NonNegativeInteger' };
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/ui/UIRenderer.ts
Outdated
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean { | ||
const plane = UIRenderer._tempPlane; | ||
const transform = this._transform; | ||
const normal = plane.normal.copyFrom(transform.worldForward); | ||
plane.distance = -Vector3.dot(normal, transform.worldPosition); | ||
const curDistance = ray.intersectPlane(plane); | ||
if (curDistance >= 0 && curDistance < distance) { | ||
const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30); | ||
const worldMatrixInv = UIRenderer._tempMat; | ||
Matrix.invert(transform.worldMatrix, worldMatrixInv); | ||
const localPosition = UIRenderer._tempVec31; | ||
Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition); | ||
if (this._hitTest(localPosition)) { | ||
out.distance = curDistance; | ||
out.entity = this._entity; | ||
out.component = this; | ||
out.normal.copyFrom(normal); | ||
out.point.copyFrom(hitPointWorld); | ||
return true; | ||
} | ||
} | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize raycast performance and readability.
The raycast implementation can be optimized for better performance and readability:
- Cache the plane intersection result
- Use early returns for better flow control
- Destructure transform properties
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean {
const plane = UIRenderer._tempPlane;
- const transform = this._transform;
+ const { worldForward, worldMatrix, worldPosition } = this._transform;
- const normal = plane.normal.copyFrom(transform.worldForward);
+ const normal = plane.normal.copyFrom(worldForward);
- plane.distance = -Vector3.dot(normal, transform.worldPosition);
+ plane.distance = -Vector3.dot(normal, worldPosition);
const intersectionDistance = ray.intersectPlane(plane);
- if (curDistance >= 0 && curDistance < distance) {
- const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30);
- const worldMatrixInv = UIRenderer._tempMat;
- Matrix.invert(transform.worldMatrix, worldMatrixInv);
+ if (intersectionDistance < 0 || intersectionDistance >= distance) {
+ return false;
+ }
+ const hitPointWorld = ray.getPoint(intersectionDistance, UIRenderer._tempVec30);
+ const worldMatrixInv = UIRenderer._tempMat;
+ Matrix.invert(worldMatrix, worldMatrixInv);
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/ui/UIRenderer.ts
Outdated
switch (flag) { | ||
case EntityModifyFlags.SiblingIndex: | ||
this._rootCanvas && (this._rootCanvas._hierarchyDirty = true); | ||
break; | ||
case EntityModifyFlags.UICanvasEnableInScene: | ||
case EntityModifyFlags.Parent: | ||
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity); | ||
rootCanvas && (rootCanvas._hierarchyDirty = true); | ||
UIUtils.registerElementToCanvas(this, rootCanvas); | ||
UIUtils.registerEntityListener(this); | ||
case EntityModifyFlags.UIGroupEnableInScene: | ||
UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity)); | ||
break; | ||
default: | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix switch statement safety issues.
The switch statement has several issues that need to be addressed:
- Case fallthrough between
Parent
andUIGroupEnableInScene
- Variable declaration scope
- Assignments within expressions
switch (flag) {
case EntityModifyFlags.SiblingIndex: {
- this._rootCanvas && (this._rootCanvas._hierarchyDirty = true);
+ if (this._rootCanvas) {
+ this._rootCanvas._hierarchyDirty = true;
+ }
break;
}
case EntityModifyFlags.UICanvasEnableInScene:
- case EntityModifyFlags.Parent:
+ case EntityModifyFlags.Parent: {
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity);
- rootCanvas && (rootCanvas._hierarchyDirty = true);
+ if (rootCanvas) {
+ rootCanvas._hierarchyDirty = true;
+ }
UIUtils.registerElementToCanvas(this, rootCanvas);
UIUtils.registerEntityListener(this);
+ break;
+ }
case EntityModifyFlags.UIGroupEnableInScene:
UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity));
break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
switch (flag) { | |
case EntityModifyFlags.SiblingIndex: | |
this._rootCanvas && (this._rootCanvas._hierarchyDirty = true); | |
break; | |
case EntityModifyFlags.UICanvasEnableInScene: | |
case EntityModifyFlags.Parent: | |
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity); | |
rootCanvas && (rootCanvas._hierarchyDirty = true); | |
UIUtils.registerElementToCanvas(this, rootCanvas); | |
UIUtils.registerEntityListener(this); | |
case EntityModifyFlags.UIGroupEnableInScene: | |
UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity)); | |
break; | |
default: | |
break; | |
} | |
switch (flag) { | |
case EntityModifyFlags.SiblingIndex: { | |
if (this._rootCanvas) { | |
this._rootCanvas._hierarchyDirty = true; | |
} | |
break; | |
} | |
case EntityModifyFlags.UICanvasEnableInScene: | |
case EntityModifyFlags.Parent: { | |
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity); | |
if (rootCanvas) { | |
rootCanvas._hierarchyDirty = true; | |
} | |
UIUtils.registerElementToCanvas(this, rootCanvas); | |
UIUtils.registerEntityListener(this); | |
break; | |
} | |
case EntityModifyFlags.UIGroupEnableInScene: | |
UIUtils.registerElementToGroup(this, UIUtils.getGroupInParents(this._entity)); | |
break; | |
default: | |
break; | |
} |
🧰 Tools
🪛 Biome
[error] 158-158: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 162-162: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 163-163: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 161-165: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIRenderer.ts
Outdated
//@todo: Always update world positions to buffer, should opt | ||
// super._updateTransformShaderData(context, onlyMVP, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Commented super call should be uncommented to match other renderer implementations
The codebase analysis shows that:
- The base
Renderer
class implements_updateTransformShaderData
- Other renderer classes (SpriteRenderer, TextRenderer, ParticleRenderer, etc.) consistently call
super._updateTransformShaderData
with the same TODO comment - UIRenderer is the only implementation that has commented out the super call while still having the same TODO
The commented super call should be uncommented to maintain consistency with other renderer implementations and ensure proper transform updates. The optimization mentioned in the TODO can be addressed separately across all renderers.
🔗 Analysis chain
Address TODO comment and clarify super call.
There's a TODO comment about optimizing world position updates and a commented-out super call that needs to be addressed:
- Implement the optimization for world position updates
- Clarify whether the super call is necessary
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if the super class implementation is different
ast-grep --pattern $'class Renderer {
$$$
_updateTransformShaderData(context: $_) {
$$$
}
$$$
}'
Length of output: 109
Script:
#!/bin/bash
# Let's try a broader search for the base class and its method
ast-grep --pattern '_updateTransformShaderData'
# Also search for any related files
fd -e ts -e js "Renderer"
# And check the specific file content
rg -A 5 -B 5 "_updateTransformShaderData"
Length of output: 9204
packages/core/src/ui/UICanvas.ts
Outdated
// @ts-ignore | ||
this._referenceResolution._onValueChanged = this._onReferenceResolutionChanged; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove @ts-ignore by extending Vector2 type.
Using @ts-ignore suppresses type checking and can hide potential issues. Create a proper type definition instead.
Create an interface extending Vector2:
interface Vector2WithCallback extends Vector2 {
_onValueChanged?: () => void;
}
Then update the property type:
- private _referenceResolution: Vector2 = new Vector2(800, 600);
+ private _referenceResolution: Vector2WithCallback = new Vector2(800, 600);
packages/core/src/ui/UICanvas.ts
Outdated
set referenceResolution(val: Vector2) { | ||
const { _referenceResolution: referenceResolution } = this; | ||
if (referenceResolution === val) return; | ||
(referenceResolution.x !== val.x || referenceResolution.y !== val.y) && referenceResolution.copyFrom(val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve readability by avoiding assignments in expressions.
Using assignments within expressions makes the code harder to read and understand.
Apply this diff to improve readability:
- (referenceResolution.x !== val.x || referenceResolution.y !== val.y) && referenceResolution.copyFrom(val);
+ if (referenceResolution.x !== val.x || referenceResolution.y !== val.y) {
+ referenceResolution.copyFrom(val);
+ }
- child.isActive && (depth = this._walk(child, elements, depth));
+ if (child.isActive) {
+ depth = this._walk(child, elements, depth);
+ }
Also applies to: 357-357
this.setMaterial(engine._basicResources.textDefaultMaterial); | ||
//@ts-ignore | ||
this._color._onValueChanged = this._onColorChanged.bind(this); | ||
this._color._onValueChanged = this._onColorChange.bind(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid type assertion in color change callback binding.
The type assertion (@ts-ignore
) suggests a potential type safety issue. Consider properly typing the color object or implementing a proper event system.
- //@ts-ignore
- this._color._onValueChanged = this._onColorChange.bind(this);
+ // Option 1: Define proper event interface
+ interface ColorWithCallback extends Color {
+ _onValueChanged?: () => void;
+ }
+ (this._color as ColorWithCallback)._onValueChanged = this._onColorChange.bind(this);
+
+ // Option 2: Use event emitter pattern
+ this._color.on('change', this._onColorChange.bind(this));
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/Entity.ts
Outdated
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | ||
(this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
_unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | ||
this._updateFlagManager?.removeListener(onChange); | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
_dispatchModify(flag: EntityModifyFlags): void { | ||
this._updateFlagManager?.dispatch(flag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix assignment in expression for better readability.
The modify listener registration contains an assignment within an expression which could be confusing.
- (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange);
+ if (!this._updateFlagManager) {
+ this._updateFlagManager = new UpdateFlagManager();
+ }
+ this._updateFlagManager.addListener(onChange);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
(this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); | |
} | |
/** | |
* @internal | |
*/ | |
_unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
this._updateFlagManager?.removeListener(onChange); | |
} | |
/** | |
* @internal | |
*/ | |
_dispatchModify(flag: EntityModifyFlags): void { | |
this._updateFlagManager?.dispatch(flag); | |
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
if (!this._updateFlagManager) { | |
this._updateFlagManager = new UpdateFlagManager(); | |
} | |
this._updateFlagManager.addListener(onChange); | |
} | |
/** | |
* @internal | |
*/ | |
_unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
this._updateFlagManager?.removeListener(onChange); | |
} | |
/** | |
* @internal | |
*/ | |
_dispatchModify(flag: EntityModifyFlags): void { | |
this._updateFlagManager?.dispatch(flag); |
🧰 Tools
🪛 Biome
[error] 612-612: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
override _processRaycast(scenes: readonly Scene[], pointer: Pointer): void { | ||
const { _tempRay: ray } = PointerEventEmitter; | ||
const hitResult = this._hitResult; | ||
const { position } = pointer; | ||
const { x, y } = position; | ||
for (let i = scenes.length - 1; i >= 0; i--) { | ||
const scene = scenes[i]; | ||
if (!scene.isActive || scene.destroyed) continue; | ||
const { _componentsManager: componentsManager } = scene; | ||
|
||
/** Overlay Canvas */ | ||
let canvasElements = componentsManager._overlayCanvases; | ||
ray.origin.set(position.x, position.y, 1); | ||
ray.direction.set(0, 0, -1); | ||
for (let j = canvasElements.length - 1; j >= 0; j--) { | ||
if (canvasElements.get(j).raycast(ray, hitResult)) { | ||
this._updateRaycast(<Component>hitResult.component, pointer); | ||
return; | ||
} | ||
} | ||
|
||
const cameras = componentsManager._activeCameras; | ||
for (let j = cameras.length - 1; j >= 0; j--) { | ||
const camera = cameras.get(j); | ||
if (camera.renderTarget) continue; | ||
const { pixelViewport } = camera; | ||
if ( | ||
x < pixelViewport.x || | ||
y < pixelViewport.y || | ||
x > pixelViewport.x + pixelViewport.width || | ||
y > pixelViewport.y + pixelViewport.height | ||
) { | ||
continue; | ||
} | ||
camera.screenPointToRay(pointer.position, ray); | ||
|
||
/** Other canvases */ | ||
const cameraPosition = camera.entity.transform.position; | ||
/** Sort by rendering order */ | ||
canvasElements = componentsManager._canvases; | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
canvasElements.get(k)._updateSortDistance(cameraPosition); | ||
} | ||
canvasElements.sort((a, b) => a.sortOrder - b.sortOrder || a._sortDistance - b._sortDistance); | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
canvasElements.get(k)._canvasIndex = k; | ||
} | ||
const farClipPlane = camera.farClipPlane; | ||
/** Post-rendering first detection */ | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
const canvas = canvasElements.get(k); | ||
if (canvas.renderCamera !== camera) continue; | ||
if (canvas.raycast(ray, hitResult, farClipPlane)) { | ||
this._updateRaycast(<Component>hitResult.component, pointer); | ||
return; | ||
} | ||
} | ||
if (camera.clearFlags & CameraClearFlags.Color) { | ||
this._updateRaycast(null); | ||
return; | ||
} | ||
} | ||
} | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider refactoring the _processRaycast
method to improve readability and maintainability.
The _processRaycast
method is quite complex with multiple nested loops and conditions. Breaking it down into smaller, focused methods would enhance the code structure and make it easier to understand and maintain.
Suggestions:
- Extract the logic for processing overlay canvases into a separate method.
- Move the code for handling camera-specific processing to a dedicated method.
- Consider creating helper methods for common tasks like sorting canvases and updating the raycast.
By decomposing the method into smaller, single-responsibility functions, the overall readability and maintainability of the code will improve.
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
packages/core/src/ui/Image.ts
Outdated
protected override _hitTest(localPosition: Vector3): boolean { | ||
let { x, y } = localPosition; | ||
const uiTransform = <UITransform>this._transform; | ||
const { x: width, y: height } = uiTransform.size; | ||
const { x: pivotX, y: pivotY } = uiTransform.pivot; | ||
const { x: paddingLeft, y: paddingBottom, z: paddingRight, w: paddingTop } = this.raycastPadding; | ||
if ( | ||
x < -width * pivotX + paddingLeft || | ||
x > width * (1 - pivotX) - paddingRight || | ||
y < -height * pivotY + paddingTop || | ||
y > height * (1 - pivotY) - paddingBottom | ||
) { | ||
return false; | ||
} | ||
const alphaHitTestMinimumThreshold = this._alphaHitTestMinimumThreshold; | ||
if (alphaHitTestMinimumThreshold <= 0) { | ||
return true; | ||
} | ||
const texture = this.sprite?.texture; | ||
if (!texture) { | ||
return false; | ||
} | ||
const uv = Image._tempVec2; | ||
if (!this._getUVByLocalPosition(localPosition, uv)) { | ||
return false; | ||
} | ||
const pixel = Image._tempUnit8Array; | ||
texture.getPixelBuffer(Math.floor(uv.x * texture.width), Math.floor(uv.y * texture.height), 1, 1, 0, pixel); | ||
if (pixel[3] >= alphaHitTestMinimumThreshold * 255) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Potential performance impact in _hitTest
method
Using texture.getPixelBuffer
inside the _hitTest
method may introduce performance overhead, especially if the method is called frequently. Consider caching the pixel data or implementing an alternative approach to minimize performance impact.
To optimize, you might consider:
+// Cache the pixel data if possible to reduce repeated texture reads
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 19
🧹 Outside diff range and nitpick comments (24)
packages/core/src/ui/interface/IUIGroupable.ts (1)
8-8
: Consider implementing a formal event system instead of internal callbacks.The
_onGroupModify
method appears to be an internal callback for group modifications. This approach might be improved by:
- Using a formal event system
- Providing a clearer public API
- Better encapsulating implementation details
Consider this alternative approach:
- _onGroupModify(flag: GroupModifyFlags): void; + onGroupChanged(event: GroupChangeEvent): void;Where
GroupChangeEvent
could be a well-defined event object:interface GroupChangeEvent { type: GroupChangeType; source: UIGroup; // other relevant event data }This would:
- Provide a clearer contract for group-related changes
- Follow standard event handling patterns
- Be more maintainable and extensible
packages/core/src/ui/interface/IUIElement.ts (2)
4-11
: Add JSDoc documentation to improve code maintainability.The interface and its members lack documentation. Consider adding JSDoc comments to describe:
- The purpose and responsibility of the
IUIElement
interface- The role of each property and method
- Parameter and return type descriptions for
_onEntityModify
Example documentation:
/** * Represents a UI element within the rendering engine. * Defines the contract for managing UI element lifecycle and hierarchy. */ export interface IUIElement { /** The entity associated with this UI element */ _entity: Entity; /** Array of parent entities in the hierarchy */ _parents: Entity[]; /** Reference to the root canvas containing this element */ _rootCanvas: UICanvas; /** Index position of this element within the canvas */ _indexInCanvas: number; /** * Handles modifications to the associated entity * @param flag - Flags indicating the type of entity modification */ _onEntityModify(flag: EntityModifyFlags): void; }
4-11
: Consider adding essential lifecycle methods.The interface could benefit from additional lifecycle methods to handle common UI element operations:
Consider adding these methods:
export interface IUIElement { // ... existing members ... /** Called when the element is mounted to the DOM */ _onMount(): void; /** Called when the element is unmounted from the DOM */ _onUnmount(): void; /** * Called when the element's visibility changes * @param visible - New visibility state */ _onVisibilityChange(visible: boolean): void; /** * Called when the element needs to update its layout * @param force - Whether to force a full layout update */ _onLayout(force?: boolean): void; }packages/core/src/ui/index.ts (1)
5-11
: Consider grouping related UI components.The exports are logically organized, but could benefit from clearer visual separation between infrastructure (UICanvas, UIRenderer, UITransform) and UI elements (Button, Image, Text).
Consider adding comments to separate these groups:
export { UICanvas } from "./UICanvas"; export { UIRenderer } from "./UIRenderer"; export { UITransform } from "./UITransform"; + +// UI Elements export { Button } from "./Button"; export { Image } from "./Image"; export { Text } from "./Text";packages/core/src/ui/interactive/UIInteractive.ts (2)
41-50
: Consider extracting runtime interactive state logic.The setter contains complex state management logic that could be moved to a separate method for better maintainability. Also, the optional chaining could lead to unexpected behavior if
_group
is undefined.Consider this refactor:
set interactive(value: boolean) { if (this._interactive !== value) { this._interactive = value; - const runtimeInteractive = value && this._group?._getGlobalInteractive(); - if (this._runtimeInteractive !== runtimeInteractive) { - this._runtimeInteractive = runtimeInteractive; - this._updateState(true); - } + this._updateRuntimeInteractive(value); } } + +private _updateRuntimeInteractive(value: boolean): void { + const globalInteractive = this._group?._getGlobalInteractive() ?? true; + const runtimeInteractive = value && globalInteractive; + if (this._runtimeInteractive !== runtimeInteractive) { + this._runtimeInteractive = runtimeInteractive; + this._updateState(true); + } +}
147-155
: Improve group modification logic.The group modification logic has similar issues to the interactive setter with optional chaining and complex state management.
Consider this refactor:
_onGroupModify(flag: GroupModifyFlags): void { if (flag & GroupModifyFlags.Interactive) { - const runtimeInteractive = this._interactive && (this._group?._getGlobalInteractive() || true); - if (this._runtimeInteractive !== runtimeInteractive) { - this._runtimeInteractive = runtimeInteractive; - this._updateState(true); - } + this._updateRuntimeInteractive(this._interactive); } }packages/core/src/ui/UIUtils.ts (2)
1-16
: Consider using a singleton pattern instead of static members.While static analysis suggests converting to standalone functions, the shared state (
_renderQueue
,_virtualCamera
,_viewport
) suggests this class would benefit from a singleton pattern. This would provide better encapsulation and testability while maintaining the same usage pattern.Example implementation:
export class UIUtils { private static instance: UIUtils; private _renderQueue: RenderQueue; private _virtualCamera: VirtualCamera; private _viewport: Vector4; private constructor() { // Initialize members } static getInstance(): UIUtils { if (!UIUtils.instance) { UIUtils.instance = new UIUtils(); } return UIUtils.instance; } // Convert static methods to instance methods }
128-152
: Improve readability of matrix operations.The direct matrix element assignments are hard to read and maintain. Consider using matrix utility methods or adding explanatory comments:
- (projectE[0] = 2 / canvas.width), (projectE[5] = 2 / canvas.height), (projectE[10] = 0); + // Set orthographic projection matrix elements + projectE[0] = 2 / canvas.width; // Scale X + projectE[5] = 2 / canvas.height; // Scale Y + projectE[10] = 0; // Flatten Z - (viewE[12] = -transform.position.x), (viewE[13] = -transform.position.y); + // Set view translation + viewE[12] = -transform.position.x; // Translate X + viewE[13] = -transform.position.y; // Translate YConsider creating helper methods for these matrix operations:
private static setOrthographicProjection(matrix: Matrix, width: number, height: number): void { const elements = matrix.elements; elements[0] = 2 / width; elements[5] = 2 / height; elements[10] = 0; } private static setViewTranslation(matrix: Matrix, position: Vector3): void { const elements = matrix.elements; elements[12] = -position.x; elements[13] = -position.y; }🧰 Tools
🪛 Biome
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 132-132: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 132-132: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
packages/core/src/ui/UIRenderer.ts (2)
22-28
: Consider implementing object pooling for temporary objects.The static temporary objects could be managed through an object pool to reduce garbage collection pressure during frequent UI updates.
Consider implementing an object pool:
class UIRendererObjectPool { private static _vec3Pool: Vector3[] = []; private static _matrixPool: Matrix[] = []; static acquireVector3(): Vector3 { return this._vec3Pool.pop() || new Vector3(); } static releaseVector3(vec: Vector3): void { vec.set(0, 0, 0); this._vec3Pool.push(vec); } }
200-222
: Optimize raycast implementation.The raycast method could be optimized by:
- Caching the plane intersection result
- Using early returns for better readability
- Using more descriptive variable names
Consider this optimization:
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean { const plane = UIRenderer._tempPlane; const { transform } = this._entity; const normal = plane.normal.copyFrom(transform.worldForward); plane.distance = -Vector3.dot(normal, transform.worldPosition); - const curDistance = ray.intersectPlane(plane); + const intersectionDistance = ray.intersectPlane(plane); - if (curDistance >= 0 && curDistance < distance) { + if (intersectionDistance < 0 || intersectionDistance >= distance) { + return false; + } const hitPointWorld = ray.getPoint(intersectionDistance, UIRenderer._tempVec30); const worldMatrixInv = UIRenderer._tempMat; Matrix.invert(transform.worldMatrix, worldMatrixInv); const localPosition = UIRenderer._tempVec31; Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition); - if (this._hitTest(localPosition)) { - out.distance = curDistance; - out.entity = this._entity; - out.component = this; - out.normal.copyFrom(normal); - out.point.copyFrom(hitPointWorld); - return true; - } - return false; + if (!this._hitTest(localPosition)) { + return false; + } + + out.distance = intersectionDistance; + out.entity = this._entity; + out.component = this; + out.normal.copyFrom(normal); + out.point.copyFrom(hitPointWorld); + return true;packages/core/src/2d/text/TextUtils.ts (2)
99-104
: Add JSDoc documentation for the updated method signature.Please add documentation for the new parameters to improve maintainability and developer experience.
+/** + * Measures text with word wrapping enabled. + * @param renderer - The text renderer or Text component + * @param rendererWidth - The maximum width for text wrapping + * @param rendererHeight - The height of the renderer + * @param lineSpacing - The spacing between lines + * @returns TextMetrics containing the measured text dimensions and line information + */ static measureTextWithWrap(
276-280
: Add JSDoc documentation for the updated method signature.Please add documentation for the new parameters to improve maintainability.
+/** + * Measures text without word wrapping. + * @param renderer - The text renderer or Text component + * @param rendererHeight - The height of the renderer + * @param lineSpacing - The spacing between lines + * @returns TextMetrics containing the measured text dimensions and line information + */ static measureTextWithoutWrap(packages/core/src/ui/UICanvas.ts (1)
349-351
: Consider using type predicates for safer type casting.The current type casting could be made safer by using TypeScript type predicates:
function isUIGraphics(component: Component): component is IUIGraphics { return component._componentType === ComponentType.UIRenderer; }Then update the code:
- if (component.enabled && component._componentType === ComponentType.UIRenderer) { - (component as unknown as IUIGraphics).depth = depth; - elements[depth] = component as unknown as IUIGraphics; + if (component.enabled && isUIGraphics(component)) { + component.depth = depth; + elements[depth] = component;packages/core/src/2d/text/TextRenderer.ts (3)
Line range hint
66-74
: Optimize color setter to avoid unnecessary updates.The color setter should check individual components to avoid triggering unnecessary updates when the same color values are set.
set color(value: Color) { - if (this._color !== value) { + if (this._color.r !== value.r || + this._color.g !== value.g || + this._color.b !== value.b || + this._color.a !== value.a) { this._color.copyFrom(value); } }
Line range hint
473-616
: Refactor_updateLocalData
for better maintainability.This method is quite complex and handles multiple responsibilities. Consider breaking it down into smaller, focused methods:
- Text measurement
- Layout calculation
- Chunk management
Suggested structure:
private _updateLocalData(): void { if (!this._calculateTextMetrics()) { return; } this._calculateLayout(); this._updateChunks(); this._setDirtyFlagFalse(RendererUpdateFlags.LocalPositionAndBounds); } private _calculateTextMetrics(): boolean { // Text measurement logic } private _calculateLayout(): void { // Layout calculation logic } private _updateChunks(): void { // Chunk management logic }
694-704
: Consider explicit enum inheritance for clarity.While the enum extends
RendererUpdateFlags
as per the comment, consider making this relationship more explicit in the code.enum TextRendererUpdateFlags { // Base flags from RendererUpdateFlags LocalPosition = RendererUpdateFlags.LocalPosition, WorldPosition = RendererUpdateFlags.WorldPosition, LocalBounds = RendererUpdateFlags.LocalBounds, WorldBounds = RendererUpdateFlags.WorldBounds, // Text-specific flags SubFont = 0x10, Color = 0x20, FontAllPositionAndBounds = SubFont | LocalPosition | WorldPosition | LocalBounds | WorldBounds, All = FontAllPositionAndBounds | Color }packages/core/src/Entity.ts (3)
107-110
: Consider adding transform setter validation.While the getter is correctly implemented, consider adding a setter with validation to ensure the transform's integrity, especially since it's a critical component for entity positioning.
get transform(): Transform { return this._transform; } +set transform(value: Transform) { + if (!value) { + throw new Error("Transform cannot be null or undefined"); + } + this._transform = value; +}
790-797
: Document EntityModifyFlags enum values.The new enum lacks documentation for its values, which is important for maintainability.
export enum EntityModifyFlags { + /** Flag indicating parent entity has changed */ Parent = 0x1, + /** Flag indicating sibling index has changed */ SiblingIndex = 0x2, + /** Flag indicating UI canvas was enabled in scene */ UICanvasEnableInScene = 0x4, + /** Flag indicating UI canvas was disabled in scene */ UICanvasDisableInScene = 0x8, + /** Flag indicating UI group was enabled in scene */ UIGroupEnableInScene = 0x10, + /** Flag indicating UI group was disabled in scene */ UIGroupDisableInScene = 0x20 }
783-783
: Consider caching matrix inversion.Matrix inversion is computationally expensive. Consider caching the result if this method is called frequently.
if (this._inverseWorldMatFlag.flag) { + // Cache the previous matrix for comparison + const prevMatrix = this._transform.worldMatrix.clone(); Matrix.invert(this._transform.worldMatrix, this._invModelMatrix); this._inverseWorldMatFlag.flag = false; + // Store the matrix used for inversion to detect changes + this._lastInvertedMatrix = prevMatrix; }packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (2)
168-190
: Consider refactoring duplicated path comparison logicThe logic for comparing paths to find common ancestors is duplicated in multiple methods (
_updateRaycast
,_processUp
, and_processLeave
). Refactoring this code into a helper method would improve maintainability and reduce code duplication.Do you want me to generate the refactored code or open a GitHub issue to track this task?
229-229
: Define magic number as a constant for better maintainabilityThe magic number
1024
is used to prevent infinite loops by limiting the iteration count. Defining this number as a constant with a descriptive name enhances code readability and makes future adjustments easier.Apply this diff to define the magic number as a constant:
- for (; i < 1024 && !!entity && entity !== rootEntity; i++) { + const MAX_PATH_DEPTH = 1024; + for (; i < MAX_PATH_DEPTH && !!entity && entity !== rootEntity; i++) {packages/core/src/ui/Image.ts (1)
21-23
: Consider moving static properties to the end of the class.To improve readability and follow common conventions, consider moving the static properties
_tempVec2
and_tempUnit8Array
to the end of the class definition.packages/core/src/ui/Text.ts (2)
225-225
: Avoid using assignment expressions.Using assignments within expressions can be confusing and is often considered a code smell. It's better to separate the assignment from the expression for clarity and maintainability.
Refactor the code to avoid assignment expressions. For example:
- this._subFont && (this._subFont = null); + if (this._subFont) { + this._subFont = null; + }Apply similar changes to the other instances of assignment expressions.
Also applies to: 466-467, 477-477, 479-479, 566-566, 568-568
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
612-616
: Consider making theTextChunk
class properties readonly.Since the
TextChunk
class is an internal data structure used by theText
class, consider making its properties readonly to prevent accidental modifications from outside the class.Apply the following changes:
class TextChunk { - charRenderInfos = new Array<CharRenderInfo>(); + readonly charRenderInfos = new Array<CharRenderInfo>(); - subChunk: SubPrimitiveChunk; + readonly subChunk: SubPrimitiveChunk; - texture: Texture2D; + readonly texture: Texture2D; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (17)
packages/core/src/2d/text/TextRenderer.ts
(20 hunks)packages/core/src/2d/text/TextUtils.ts
(11 hunks)packages/core/src/Entity.ts
(10 hunks)packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
(1 hunks)packages/core/src/ui/Image.ts
(1 hunks)packages/core/src/ui/Text.ts
(1 hunks)packages/core/src/ui/UICanvas.ts
(1 hunks)packages/core/src/ui/UIGroup.ts
(1 hunks)packages/core/src/ui/UIRenderer.ts
(1 hunks)packages/core/src/ui/UIUtils.ts
(1 hunks)packages/core/src/ui/index.ts
(1 hunks)packages/core/src/ui/interactive/UIInteractive.ts
(1 hunks)packages/core/src/ui/interactive/transition/SpriteTransition.ts
(1 hunks)packages/core/src/ui/interactive/transition/Transition.ts
(1 hunks)packages/core/src/ui/interface/IUIElement.ts
(1 hunks)packages/core/src/ui/interface/IUIGraphics.ts
(1 hunks)packages/core/src/ui/interface/IUIGroupable.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/core/src/ui/interactive/transition/SpriteTransition.ts
- packages/core/src/ui/interactive/transition/Transition.ts
- packages/core/src/ui/interface/IUIGraphics.ts
🧰 Additional context used
🪛 Biome
packages/core/src/Entity.ts
[error] 612-612: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/Text.ts
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 552-552: This is an unexpected use of the debugger statement.
Unsafe fix: Remove debugger statement
(lint/suspicious/noDebugger)
[error] 566-566: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 568-568: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UICanvas.ts
[error] 194-194: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 211-211: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 357-357: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 471-472: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 481-482: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 483-485: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts
[error] 105-105: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 110-110: Avoid redundant double-negation.
It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation
(lint/complexity/noExtraBooleanCast)
[error] 153-153: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts
[error] 158-158: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 162-162: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 163-163: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 161-165: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIUtils.ts
[error] 16-153: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 130-130: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 131-131: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 131-131: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 132-132: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 132-132: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
packages/core/src/ui/interactive/UIInteractive.ts
[error] 132-134: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
🔇 Additional comments (49)
packages/core/src/ui/interface/IUIGroupable.ts (2)
1-2
: LGTM! Imports are well-organized.
The imports are properly structured and use appropriate relative paths.
4-9
: Verify the impact of interface changes across the codebase.
Since this is a new interface that will be implemented by UI components, we should verify its usage pattern across the codebase.
✅ Verification successful
Let me check the actual implementations and usages to better understand the impact.
Interface changes appear to be properly integrated across the codebase
The interface IUIGroupable
is well-integrated into the UI component hierarchy:
- Correctly implemented by
UIInteractive
class - Used as a base interface for
IUIGraphics
- Properly utilized in
UIGroup
for managing grouped elements - Integrated with
UIUtils
for group management functionality
The interface changes maintain consistency with its implementations and usages across the codebase. All required properties and methods are properly referenced.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for potential implementations and usages of IUIGroupable
echo "Searching for implementations of IUIGroupable..."
rg "implements.*IUIGroupable" -l
echo "\nSearching for imports of IUIGroupable..."
rg "import.*IUIGroupable" -l
echo "\nSearching for type references to IUIGroupable..."
rg ": IUIGroupable" -l
Length of output: 645
Script:
#!/bin/bash
# Check the implementation in UIInteractive
echo "Checking UIInteractive implementation..."
rg "implements.*IUIGroupable" -A 10 packages/core/src/ui/interactive/UIInteractive.ts
echo "\nChecking UIGroup usage of IUIGroupable..."
rg "IUIGroupable" -A 5 packages/core/src/ui/UIGroup.ts
echo "\nChecking UIUtils usage..."
rg "IUIGroupable" -A 5 packages/core/src/ui/UIUtils.ts
echo "\nChecking IUIGraphics interface..."
rg "IUIGroupable" -A 5 packages/core/src/ui/interface/IUIGraphics.ts
Length of output: 1924
packages/core/src/ui/interface/IUIElement.ts (1)
1-3
: LGTM!
The imports are correct and necessary for the interface implementation.
packages/core/src/ui/index.ts (3)
1-4
: LGTM! Well-organized core exports.
The grouping of core UI components and enums at the top of the file follows good organizational practices. The separation of enums into their own directory suggests good code organization.
1-15
: Consider adding type exports for public interfaces.
While the component exports are well-organized, consider also exporting any public interfaces or types that consumers might need when working with these UI components.
Let's check for potentially missing type exports:
#!/bin/bash
# Description: Look for interfaces that might need to be exported
# Search for public interfaces in UI-related files
echo "Checking for public interfaces in UI files..."
rg "export interface" "./packages/core/src/ui/"
12-15
: Verify transition system integration.
The transition system appears to be comprehensive, covering color, scale, and sprite transitions. However, we should verify that these transitions are properly integrated with the UI components.
Let's verify the integration:
✅ Verification successful
Transition system is properly integrated with UI components
The verification shows a well-structured transition system:
UIInteractive
class serves as the integration point, managing transitions through_transitions
array- Base
Transition
class is properly extended byColorTransition
,ScaleTransition
, andSpriteTransition
- Transitions are properly managed with methods like
getTransition
,addTransition
,removeTransition
- Transitions are automatically updated in the component lifecycle through
onUpdate
- State changes are properly propagated to all transitions via
_setState
The implementation shows a robust and type-safe transition system that's tightly integrated with the UI component lifecycle.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if transitions are properly integrated with UI components
# Test 1: Check for transition usage in UI components
echo "Checking transition usage in UI components..."
rg -l "Transition" "./packages/core/src/ui/(Button|Image|Text).ts"
# Test 2: Check if Transition is the base class for other transitions
echo "Verifying transition hierarchy..."
ast-grep --pattern 'class $_ extends Transition'
Length of output: 666
Script:
#!/bin/bash
# Let's verify the transition system integration with more precise commands
# Check for transition imports and usage in UI components
echo "Checking transition usage in UI components..."
rg -l "import.*Transition" packages/core/src/ui/
# Check implementation of transition classes
echo "Checking transition implementations..."
rg -A 5 "class.*Transition" packages/core/src/ui/interactive/transition/
# Check if UI components have transition-related methods or properties
echo "Checking UI components for transition methods..."
rg -A 3 "(transition|animate)" packages/core/src/ui/
Length of output: 6692
packages/core/src/ui/interactive/UIInteractive.ts (6)
1-36
: LGTM! Well-structured class definition with clear property organization.
The class structure follows good practices with appropriate use of decorators and clear property visibility modifiers.
52-78
: LGTM! Well-implemented transition management with type safety.
The transition management methods make good use of TypeScript generics and provide a clean API for managing transitions.
80-102
: LGTM! Clean event handling implementation.
The pointer event handlers maintain a clear separation between state tracking and updates.
107-123
: LGTM! Proper lifecycle management with clean state handling.
The lifecycle methods properly manage registration/unregistration and state cleanup.
157-174
: LGTM! Clean state management implementation.
The state management methods are well-organized with clear transition logic and state calculations.
130-140
:
Fix switch case fallthrough issue.
The cases for UICanvasEnableInScene
and Parent
are falling through to UIGroupEnableInScene
unintentionally.
🧰 Tools
🪛 Biome
[error] 132-134: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
packages/core/src/ui/UIGroup.ts (5)
1-41
: LGTM! Well-structured class definition with proper property management.
The class structure is well-organized with appropriate use of decorators and clear documentation of internal properties.
43-75
: LGTM! Well-implemented property accessors.
The getters and setters are properly implemented with appropriate validation and update triggers.
117-144
: LGTM! Thorough lifecycle management.
The lifecycle methods properly handle registration, cleanup, and garbage collection.
186-191
: LGTM! Well-designed flags enum.
The GroupModifyFlags enum is properly implemented using bit flags, allowing efficient combination of multiple flags.
153-154
: 🛠️ Refactor suggestion
Improve readability by avoiding assignment in expression.
The assignment within the logical AND expression makes the code harder to read and understand.
Apply this diff:
- replaced && (replaced._groupIndex = this._groupIndex);
+ if (replaced) {
+ replaced._groupIndex = this._groupIndex;
+ }
Likely invalid or redundant comment.
🧰 Tools
🪛 Biome
[error] 153-153: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIRenderer.ts (1)
19-20
:
Add missing ISpriteRenderer interface.
The class should implement the ISpriteRenderer interface to maintain consistency with the sprite rendering system.
-export abstract class UIRenderer extends Renderer implements IUIGraphics {
+export abstract class UIRenderer extends Renderer implements IUIGraphics, ISpriteRenderer {
Likely invalid or redundant comment.
packages/core/src/2d/text/TextUtils.ts (2)
2-2
: LGTM: Import addition aligns with new type requirements.
The addition of the Text import supports the new union type parameter (TextRenderer | Text) in the measure methods.
99-104
: Verify interface compatibility between TextRenderer and Text.
Both measure methods accept either TextRenderer or Text type. Let's verify they implement the required interface members.
Also applies to: 276-280
✅ Verification successful
Let me gather more information about both TextRenderer
and Text
classes to verify their interface compatibility.
Let me search for more specific information about the text measurement methods and required interface members.
Let me search for the required properties and methods in both classes with a different pattern.
Based on the gathered information, I can now provide the final response:
Interface compatibility between TextRenderer and Text is verified.
Both TextRenderer
and Text
classes implement the required interface members for measureTextWithWrap
:
- Both have
overflowMode
property with identical behavior (defaults toOverflowMode.Overflow
) - Both have
width
andheight
properties - Both use the same text measurement logic through
TextUtils.measureTextWithWrap
- Both handle text overflow and truncation in the same way
The shared interface is consistently implemented across both classes, making them safely interchangeable in the measureTextWithWrap
method.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for the Text class definition to verify required members
echo "Searching for Text class definition..."
ast-grep --pattern 'class Text {
$$$
_getSubFont() {
$$$
}
$$$
}'
echo "Verifying overflowMode property..."
ast-grep --pattern 'class Text {
$$$
overflowMode: $_
$$$
}'
Length of output: 314
Script:
#!/bin/bash
# Search for TextRenderer interface/class definition
echo "Searching for TextRenderer definition..."
rg "(?:interface|class|type)\s+TextRenderer" -A 10
# Search for Text class usage of measureTextWithWrap
echo "Searching for Text class measureText related methods..."
rg "class Text" -A 30
# Search for common method signatures between Text and TextRenderer
echo "Searching for common method signatures..."
ast-grep --pattern '(class Text|class TextRenderer) {
$$$
measureText($_) {
$$$
}
$$$
}'
Length of output: 36998
Script:
#!/bin/bash
# Search for measureTextWithWrap implementation and usage
echo "Searching for measureTextWithWrap implementation..."
rg "measureTextWithWrap" -A 5
# Search for required properties and methods in both classes
echo "Searching for text measurement related properties..."
ast-grep --pattern '(class Text|class TextRenderer) {
$$$
_getSubFont() {
$$$
}
$$$
overflowMode: $_
$$$
}'
# Search for common interface or type definitions
echo "Searching for TextRenderer interface..."
rg "interface.*TextRenderer" -A 10
Length of output: 8882
Script:
#!/bin/bash
# Search for overflowMode property in both classes
echo "Searching for overflowMode property..."
rg "overflowMode.*=" -B 5 -A 1
# Search for _getSubFont method in both classes
echo "Searching for _getSubFont method..."
rg "_getSubFont.*{" -B 2 -A 10
# Search for width and height properties
echo "Searching for width and height properties..."
rg "(get width\(\)|get height\(\))" -B 2 -A 5
Length of output: 18444
packages/core/src/2d/text/TextRenderer.ts (1)
35-35
:
Fix array initialization syntax.
The array initialization syntax is incorrect. Using Array<TextChunk>()
without the new
keyword doesn't create a new array instance.
-private _textChunks = Array<TextChunk>();
+private _textChunks: TextChunk[] = [];
Likely invalid or redundant comment.
packages/core/src/Entity.ts (4)
99-105
: LGTM: Property refactoring improves encapsulation.
The refactoring of transform
to a private _transform
property with a getter improves encapsulation. The addition of _updateFlagManager
enables better state change management.
205-206
: LGTM: Constructor initialization is correct.
The initialization of _transform
and registration of world change flag is properly implemented.
527-530
: LGTM: Proper cleanup in destroy method.
The cleanup of _updateFlagManager
is correctly implemented with null checks and removal of all listeners.
404-404
: Verify UI Transform inheritance.
The conditional addition of UITransform needs verification to ensure proper inheritance across the entity hierarchy.
✅ Verification successful
UI Transform inheritance is correctly implemented
The verification shows that the UI Transform inheritance is properly handled:
UITransform
extends the baseTransform
class and is automatically added to components that require it through the@dependentComponents
decorator (seen in UIRenderer and UICanvas).- The inheritance check in
Entity.createChild()
ensures that if a parent entity has aUITransform
, its children will also get aUITransform
component. - The implementation maintains consistency in the UI component hierarchy by propagating the appropriate transform type from parent to child entities.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check UITransform usage patterns
# Expected: Consistent UITransform inheritance in parent-child relationships
# Check UITransform inheritance patterns
ast-grep --pattern 'class $_ extends $_ {
$$$
addComponent($_) {
$$$
UITransform
$$$
}
$$$
}'
Length of output: 115
Script:
#!/bin/bash
# Let's try a different approach to analyze UITransform usage
# Search for UITransform related code patterns
rg "UITransform" -A 5 -B 5
# Also check for specific inheritance patterns
ast-grep --pattern 'this._transform instanceof UITransform'
# Look for component addition patterns
ast-grep --pattern 'addComponent($_)'
Length of output: 21016
packages/core/src/input/pointer/emitter/PointerUIEventEmitter.ts (3)
102-102
: ****
🧰 Tools
🪛 Biome
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 102-102: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
222-222
: ****
🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
167-168
: ****
packages/core/src/ui/Image.ts (19)
1-19
: LGTM!
The imports are well-organized and cover the necessary dependencies for the Image
class implementation.
20-20
: LGTM!
The class declaration is correct, extending UIRenderer
and implementing the ISpriteRenderer
interface.
24-35
: LGTM!
The private properties are well-defined, with appropriate types and default values. The use of @ignoreClone
and @assignmentClone
decorators is consistent.
37-43
: LGTM!
The getter and setter for alphaHitTestMinimumThreshold
are implemented correctly, with proper clamping of the input value.
45-71
: LGTM!
The getter and setter for drawMode
are implemented correctly. The setter updates the _assembler
based on the selected draw mode and resets the sprite data.
73-87
: LGTM!
The getter and setter for tileMode
are implemented correctly. The setter updates the dirty flag when the draw mode is set to Tiled
.
89-104
: LGTM!
The getter and setter for tiledAdaptiveThreshold
are implemented correctly, with proper clamping of the input value and updating the dirty flag when the draw mode is set to Tiled
.
106-130
: LGTM!
The getter and setter for sprite
are implemented correctly. The setter handles resource reference counting, event listener management, and updating the shader data.
132-143
: LGTM!
The constructor initializes the necessary properties and binds event listeners correctly.
145-178
: LGTM!
The _hitTest
method is implemented correctly, handling the hit testing based on the sprite's bounds and alpha threshold.
180-191
: LGTM!
The _updateLocalBounds
method is implemented correctly, updating the local bounds based on the sprite's size and pivot.
193-248
: LGTM!
The _render
method is implemented correctly, handling the rendering process based on the sprite's properties and updating the necessary data when dirty flags are set.
250-255
: LGTM!
The _canBatch
method is implemented correctly, delegating the batching decision to BatchUtils.canBatchSprite
.
257-262
: LGTM!
The _batch
method is implemented correctly, delegating the batching process to BatchUtils.batchFor2D
.
264-283
: LGTM!
The _onTransformChanged
method is implemented correctly, updating the dirty flags based on the transform modification type.
285-298
: LGTM!
The _onDestroy
method is implemented correctly, handling resource cleanup and event listener removal.
300-341
: LGTM!
The _onSpriteChange
method is implemented correctly, updating the dirty flags based on the sprite modification type.
343-356
: LGTM!
The _getUVByLocalPosition
method is implemented correctly, delegating the UV calculation to the appropriate sprite assembler based on the draw mode.
359-371
: LGTM!
The ImageUpdateFlags
enum is defined correctly, extending the UIRendererUpdateFlags
and providing additional flags specific to the Image
class.
packages/core/src/ui/Text.ts (2)
25-609
: The Text
class implementation looks solid and comprehensive. Great work!
The class provides a robust set of properties and methods for managing text rendering in a UI context. The code is well-structured, follows best practices, and includes appropriate optimizations for efficient rendering.
Some key highlights:
- Proper handling of resource management through reference counting
- Efficient rendering using text chunks and sub-render elements
- Comprehensive text layout and alignment options
- Effective use of dirty flags for updating rendering state
- Well-organized and readable code structure
Overall, the Text
class seems to be a valuable addition to the UI rendering system.
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 552-552: This is an unexpected use of the debugger statement.
Unsafe fix: Remove debugger statement
(lint/suspicious/noDebugger)
[error] 566-566: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 568-568: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
1-628
: Verify the integration of the Text
class with other UI components.
The Text
class seems to be well-integrated with the UI rendering system, particularly with the UIRenderer
class. However, it's important to ensure that the Text
class works seamlessly with other UI components introduced in this pull request, such as UIGroup
and UICanvas
.
To verify the integration, you can perform the following checks:
-
Review the usage of the
Text
class in the context ofUIGroup
andUICanvas
classes to ensure proper rendering order and hierarchy management. -
Analyze the interaction between the
Text
class and the enhancedComponentsManager
to confirm that text rendering is efficiently handled within the overall UI hierarchy. -
Test the rendering of
Text
instances in various scenarios, such as different canvas render modes, to validate the expected behavior. -
Verify that the
Text
class correctly utilizes the rendering optimizations provided by theUIRenderer
class, such as rendering contexts and batching.
If any issues or inconsistencies are found during the verification process, please let me know, and I'll be happy to assist in resolving them.
🧰 Tools
🪛 Biome
[error] 225-225: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 466-466: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 467-467: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 477-477: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 479-479: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 552-552: This is an unexpected use of the debugger statement.
Unsafe fix: Remove debugger statement
(lint/suspicious/noDebugger)
[error] 566-566: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 568-568: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
export interface IUIGroupable extends IUIElement { | ||
_group: UIGroup; | ||
_indexInGroup: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider revising the interface design to hide implementation details.
The current design exposes internal implementation details through properties prefixed with underscores. This approach:
- Breaks encapsulation by exposing internal state
- Makes the interface tightly coupled to the implementation
- Deviates from TypeScript interface conventions
Consider this alternative design that hides implementation details:
-export interface IUIGroupable extends IUIElement {
- _group: UIGroup;
- _indexInGroup: number;
+export interface IUIGroupable extends IUIElement {
+ readonly group: UIGroup;
+ readonly indexInGroup: number;
This change:
- Removes implementation-specific underscore prefixes
- Makes properties readonly to prevent external modifications
- Better adheres to interface design principles
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export interface IUIGroupable extends IUIElement { | |
_group: UIGroup; | |
_indexInGroup: number; | |
export interface IUIGroupable extends IUIElement { | |
readonly group: UIGroup; | |
readonly indexInGroup: number; |
packages/core/src/ui/UIUtils.ts
Outdated
static registerElementToCanvas(element: IUIElement, canvas: UICanvas): void { | ||
const preCanvas = element._rootCanvas; | ||
if (preCanvas !== canvas) { | ||
element._rootCanvas = canvas; | ||
if (preCanvas) { | ||
const replaced = preCanvas._disorderedElements.deleteByIndex(element._indexInCanvas); | ||
replaced && (replaced._indexInCanvas = element._indexInCanvas); | ||
element._indexInCanvas = -1; | ||
preCanvas._hierarchyDirty = true; | ||
} | ||
if (canvas) { | ||
const disorderedElements = canvas._disorderedElements; | ||
element._indexInCanvas = disorderedElements.length; | ||
disorderedElements.add(element); | ||
canvas._hierarchyDirty = true; | ||
} | ||
} | ||
} | ||
|
||
static registerElementToGroup(element: IUIGroupable, group: UIGroup): void { | ||
const preGroup = element._group; | ||
if (preGroup !== group) { | ||
element._group = group; | ||
if (preGroup) { | ||
const replaced = preGroup._disorderedElements.deleteByIndex(element._indexInGroup); | ||
replaced && (replaced._indexInGroup = element._indexInGroup); | ||
element._indexInGroup = -1; | ||
} | ||
if (group) { | ||
const disorderedElements = group._disorderedElements; | ||
element._indexInGroup = disorderedElements.length; | ||
disorderedElements.add(element); | ||
} | ||
element._onGroupModify(GroupModifyFlags.All); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Reduce code duplication in registration methods.
The registerElementToCanvas
and registerElementToGroup
methods share similar logic. Consider extracting the common functionality into a private helper method:
private static registerElementToContainer<T extends IUIElement, C>(
element: T,
container: C,
getPreContainer: (el: T) => C,
setContainer: (el: T, c: C) => void,
getDisorderedElements: (c: C) => DisorderedArray<T>,
getIndex: (el: T) => number,
setIndex: (el: T, i: number) => void,
onModify?: (el: T) => void
): void {
const preContainer = getPreContainer(element);
if (preContainer !== container) {
setContainer(element, container);
if (preContainer) {
const replaced = getDisorderedElements(preContainer).deleteByIndex(getIndex(element));
if (replaced) {
setIndex(replaced, getIndex(element));
}
setIndex(element, -1);
}
if (container) {
const disorderedElements = getDisorderedElements(container);
setIndex(element, disorderedElements.length);
disorderedElements.add(element);
}
onModify?.(element);
}
}
🧰 Tools
🪛 Biome
[error] 53-53: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 72-72: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts
Outdated
static getRootCanvasInParent(entity: Entity): UICanvas { | ||
while (entity) { | ||
const components = entity._components; | ||
for (let i = 0, n = components.length; i < n; i++) { | ||
const component = components[i]; | ||
if ( | ||
component.enabled && | ||
component._componentType === ComponentType.UICanvas && | ||
(<UICanvas>component)._isRootCanvas | ||
) { | ||
return <UICanvas>component; | ||
} | ||
} | ||
entity = entity.parent; | ||
} | ||
return null; | ||
} | ||
|
||
static getGroupInParents(entity: Entity): UIGroup { | ||
let meetRootCanvas = false; | ||
while (entity) { | ||
const components = entity._components; | ||
for (let i = 0, n = components.length; i < n; i++) { | ||
const component = components[i]; | ||
if (component.enabled) { | ||
switch (component._componentType) { | ||
case ComponentType.UICanvas: | ||
meetRootCanvas = (<UICanvas>component)._isRootCanvas; | ||
break; | ||
case ComponentType.UIGroup: | ||
return <UIGroup>component; | ||
default: | ||
break; | ||
} | ||
} | ||
} | ||
if (meetRootCanvas) { | ||
return null; | ||
} | ||
entity = entity.parent; | ||
} | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve type safety and performance in traversal methods.
- Replace type casting with type guards for better type safety.
- Consider caching results for frequently accessed entities.
private static isUICanvas(component: any): component is UICanvas {
return component._componentType === ComponentType.UICanvas;
}
private static isUIGroup(component: any): component is UIGroup {
return component._componentType === ComponentType.UIGroup;
}
static getRootCanvasInParent(entity: Entity): UICanvas {
// Consider adding WeakMap cache for frequent lookups
private static canvasCache = new WeakMap<Entity, UICanvas>();
if (UIUtils.canvasCache.has(entity)) {
return UIUtils.canvasCache.get(entity);
}
while (entity) {
const components = entity._components;
for (const component of components) {
if (component.enabled && UIUtils.isUICanvas(component) && component._isRootCanvas) {
UIUtils.canvasCache.set(entity, component);
return component;
}
}
entity = entity.parent;
}
return null;
}
private static _renderQueue: RenderQueue; | ||
private static _virtualCamera: VirtualCamera; | ||
private static _viewport: Vector4; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Initialize static fields immediately to prevent race conditions.
Lazy initialization in static fields can lead to race conditions in concurrent scenarios. Consider initializing all fields during class definition:
- private static _renderQueue: RenderQueue;
- private static _virtualCamera: VirtualCamera;
- private static _viewport: Vector4;
+ private static _renderQueue: RenderQueue = new RenderQueue(RenderQueueType.Transparent);
+ private static _virtualCamera: VirtualCamera = new VirtualCamera();
+ private static _viewport: Vector4 = new Vector4(0, 0, 1, 1);
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/ui/UIUtils.ts
Outdated
static registerEntityListener(element: IUIElement): void { | ||
const parents = element._parents; | ||
const root = element._rootCanvas?.entity; | ||
let entity = element._entity; | ||
let index = 0; | ||
while (entity && entity !== root) { | ||
const preParent = parents[index]; | ||
if (preParent !== entity) { | ||
preParent?._unRegisterModifyListener(element._onEntityModify); | ||
parents[index] = entity; | ||
entity._registerModifyListener(element._onEntityModify); | ||
} | ||
entity = entity.parent; | ||
index++; | ||
} | ||
parents.length = index; | ||
} | ||
|
||
static unRegisterEntityListener(element: IUIElement): void { | ||
const { _parents: parents } = element; | ||
for (let i = 0, n = parents.length; i < n; i++) { | ||
parents[i]._unRegisterModifyListener(element._onEntityModify); | ||
} | ||
parents.length = 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add input validation and error handling.
The entity listener methods lack input validation and error handling. Consider adding checks for null/undefined parameters and handling edge cases:
static registerEntityListener(element: IUIElement): void {
+ if (!element) {
+ throw new Error("Element cannot be null or undefined");
+ }
const parents = element._parents;
+ if (!parents) {
+ throw new Error("Element parents array is not initialized");
+ }
// ... rest of the method
}
static unRegisterEntityListener(element: IUIElement): void {
+ if (!element || !element._parents) {
+ return; // Silent fail on cleanup
+ }
const { _parents: parents } = element;
// ... rest of the method
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
static registerEntityListener(element: IUIElement): void { | |
const parents = element._parents; | |
const root = element._rootCanvas?.entity; | |
let entity = element._entity; | |
let index = 0; | |
while (entity && entity !== root) { | |
const preParent = parents[index]; | |
if (preParent !== entity) { | |
preParent?._unRegisterModifyListener(element._onEntityModify); | |
parents[index] = entity; | |
entity._registerModifyListener(element._onEntityModify); | |
} | |
entity = entity.parent; | |
index++; | |
} | |
parents.length = index; | |
} | |
static unRegisterEntityListener(element: IUIElement): void { | |
const { _parents: parents } = element; | |
for (let i = 0, n = parents.length; i < n; i++) { | |
parents[i]._unRegisterModifyListener(element._onEntityModify); | |
} | |
parents.length = 0; | |
} | |
static registerEntityListener(element: IUIElement): void { | |
if (!element) { | |
throw new Error("Element cannot be null or undefined"); | |
} | |
const parents = element._parents; | |
if (!parents) { | |
throw new Error("Element parents array is not initialized"); | |
} | |
const root = element._rootCanvas?.entity; | |
let entity = element._entity; | |
let index = 0; | |
while (entity && entity !== root) { | |
const preParent = parents[index]; | |
if (preParent !== entity) { | |
preParent?._unRegisterModifyListener(element._onEntityModify); | |
parents[index] = entity; | |
entity._registerModifyListener(element._onEntityModify); | |
} | |
entity = entity.parent; | |
index++; | |
} | |
parents.length = index; | |
} | |
static unRegisterEntityListener(element: IUIElement): void { | |
if (!element || !element._parents) { | |
return; // Silent fail on cleanup | |
} | |
const { _parents: parents } = element; | |
for (let i = 0, n = parents.length; i < n; i++) { | |
parents[i]._unRegisterModifyListener(element._onEntityModify); | |
} | |
parents.length = 0; | |
} |
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | ||
(this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix assignment in expression.
The assignment within the expression can be confusing and should be split for better readability.
- (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange);
+ if (!this._updateFlagManager) {
+ this._updateFlagManager = new UpdateFlagManager();
+ }
+ this._updateFlagManager.addListener(onChange);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { | |
(this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); | |
} | |
_registerModifyListener(onChange: (EntityModifyFlags) => void): void { | |
if (!this._updateFlagManager) { | |
this._updateFlagManager = new UpdateFlagManager(); | |
} | |
this._updateFlagManager.addListener(onChange); | |
} |
🧰 Tools
🪛 Biome
[error] 612-612: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
override _processRaycast(scenes: readonly Scene[], pointer: Pointer): void { | ||
const { _tempRay: ray } = PointerEventEmitter; | ||
const hitResult = this._hitResult; | ||
const { position } = pointer; | ||
const { x, y } = position; | ||
for (let i = scenes.length - 1; i >= 0; i--) { | ||
const scene = scenes[i]; | ||
if (!scene.isActive || scene.destroyed) continue; | ||
const { _componentsManager: componentsManager } = scene; | ||
|
||
/** Overlay Canvas */ | ||
let canvasElements = componentsManager._overlayCanvases; | ||
ray.origin.set(position.x, position.y, 1); | ||
ray.direction.set(0, 0, -1); | ||
for (let j = canvasElements.length - 1; j >= 0; j--) { | ||
if (canvasElements.get(j).raycast(ray, hitResult)) { | ||
this._updateRaycast(<Component>hitResult.component, pointer); | ||
return; | ||
} | ||
} | ||
|
||
const cameras = componentsManager._activeCameras; | ||
for (let j = cameras.length - 1; j >= 0; j--) { | ||
const camera = cameras.get(j); | ||
if (camera.renderTarget) continue; | ||
const { pixelViewport } = camera; | ||
if ( | ||
x < pixelViewport.x || | ||
y < pixelViewport.y || | ||
x > pixelViewport.x + pixelViewport.width || | ||
y > pixelViewport.y + pixelViewport.height | ||
) { | ||
continue; | ||
} | ||
camera.screenPointToRay(pointer.position, ray); | ||
|
||
/** Other canvases */ | ||
const cameraPosition = camera.entity.transform.position; | ||
/** Sort by rendering order */ | ||
canvasElements = componentsManager._canvases; | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
canvasElements.get(k)._updateSortDistance(cameraPosition); | ||
} | ||
canvasElements.sort((a, b) => a.sortOrder - b.sortOrder || a._sortDistance - b._sortDistance); | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
canvasElements.get(k)._canvasIndex = k; | ||
} | ||
const farClipPlane = camera.farClipPlane; | ||
/** Post-rendering first detection */ | ||
for (let k = 0, n = canvasElements.length; k < n; k++) { | ||
const canvas = canvasElements.get(k); | ||
if (canvas.renderCamera !== camera) continue; | ||
if (canvas.raycast(ray, hitResult, farClipPlane)) { | ||
this._updateRaycast(<Component>hitResult.component, pointer); | ||
return; | ||
} | ||
} | ||
if (camera.clearFlags & CameraClearFlags.Color) { | ||
this._updateRaycast(null); | ||
return; | ||
} | ||
} | ||
} | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Method complexity exceeds recommended threshold
The _processRaycast
method is complex with multiple nested loops and conditions. Consider breaking it down into smaller, focused methods:
processOverlayCanvas
processCameraCanvas
handleCameraRaycast
This would improve readability and maintainability.
Do you want me to generate the refactored code or open a GitHub issue to track this task?
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
override _processDrag(pointer: Pointer): void { | ||
if (this._draggedElement) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add parameter validation and error handling
The _processDrag
method accepts a pointer
parameter without validation. Consider adding null checks and appropriate error handling to prevent runtime exceptions.
Apply this diff to add validation:
override _processDrag(pointer: Pointer): void {
+ if (!pointer) {
+ console.warn('Invalid pointer provided to _processDrag');
+ return;
+ }
if (this._draggedElement) {
Do you want me to open a GitHub issue to track this task?
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
override _processDrag(pointer: Pointer): void { | |
if (this._draggedElement) { | |
override _processDrag(pointer: Pointer): void { | |
if (!pointer) { | |
console.warn('Invalid pointer provided to _processDrag'); | |
return; | |
} | |
if (this._draggedElement) { |
} | ||
} | ||
} | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unnecessary return null;
in a void function
The method _processRaycast
is declared with a return type of void
, but it ends with return null;
. Since the function is expected to return void
, the return null;
statement is unnecessary and should be removed to prevent confusion.
Apply this diff to fix the issue:
- return null;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
return null; |
🧰 Tools
🪛 Biome
[error] 85-85: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
packages/core/src/ui/Text.ts
Outdated
} | ||
|
||
private _isTextNoVisible(): boolean { | ||
debugger; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the debugger statement.
The debugger
statement should not be present in production code as it can pause execution and affect performance.
Remove the following line:
- debugger;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
debugger; |
🧰 Tools
🪛 Biome
[error] 552-552: This is an unexpected use of the debugger statement.
Unsafe fix: Remove debugger statement
(lint/suspicious/noDebugger)
feat: seperate ui package
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
🛑 Comments failed to post (68)
packages/ui/src/component/UITransform.ts (2)
32-41:
⚠️ Potential issueAvoid accessing private properties and suppressing TypeScript errors
The use of
// @ts-ignore
to accessentity._transform
andentity._inverseWorldMatFlag
indicates that you're accessing private or protected properties. Suppressing TypeScript errors can hide potential issues and compromises type safety.Consider the following actions:
- Refactor the code to use public APIs or accessor methods: If possible, use public methods or properties provided by the
Entity
class to achieve the same functionality.- Modify the base class to expose necessary properties: If you have control over the
Entity
class, consider exposing_transform
and_inverseWorldMatFlag
through public or protected members.- Extend the
Entity
class appropriately: Create a subclass ofEntity
that exposes the needed properties in a type-safe manner.
46-47:
⚠️ Potential issueAccessing private members
_updateFlagManager
may lead to maintenance issuesDirectly accessing private members like
this._updateFlagManager
by suppressing TypeScript errors with// @ts-ignore
can lead to maintenance challenges and violates encapsulation principles.Consider the following solutions:
- Expose a protected method or property in the base class: Modify the
Transform
class to provide protected access to_updateFlagManager
.- Use public methods to dispatch updates: If available, use existing public methods to dispatch update flags without needing to access private members.
Also applies to: 52-53
packages/ui/src/component/interactive/transition/Transition.ts (4)
84-94:
⚠️ Potential issuePrevent potential division by zero in
set duration
methodIn the
set duration(value: number)
method, there is a potential division by zero whenpreDuration
is zero.At line 90:
this._countDown = value * (1 - this._countDown / preDuration);If
preDuration
is zero, dividing by zero will result inInfinity
orNaN
, causing unexpected behavior. To prevent this, add a check to ensurepreDuration
is not zero before performing the division.Apply this diff to fix the issue:
set duration(value: number) { if (value < 0) value = 0; const preDuration = this._duration; if (preDuration !== value) { this._duration = value; if (this._countDown > 0) { + if (preDuration > 0) { this._countDown = value * (1 - this._countDown / preDuration); + } else { + this._countDown = value; + } this._updateValue(); } } }
76-76:
⚠️ Potential issueEnsure
_currentValue
is initialized before useIn lines 76 and 148, the method
_applyValue
is called withthis._currentValue
, which may beundefined
if not properly initialized. This can lead to unexpected behavior or runtime errors.Initialize
this._currentValue
before it's used. Consider setting a default value during declaration or ensuring it's assigned in all code paths before invocation:protected _currentValue: T; + + constructor() { + // Initialize _currentValue with a default value, if appropriate + this._currentValue = this.normal; // or any suitable default + }Alternatively, add checks before calling
_applyValue
:if (this._currentValue !== undefined) { value?.enabled && this._applyValue(this._currentValue); }Ensuring
_currentValue
is properly initialized enhances code stability.Also applies to: 148-148
135-138: 🛠️ Refactor suggestion
Eliminate the use of
@ts-ignore
directivesUsing
@ts-ignore
in lines 135 and 137 suppresses TypeScript compiler errors, which can hide potential issues and reduce type safety.Replace
@ts-ignore
with proper type checks or type assertions to maintain type safety. Here's how you can modify the code:protected _onStateValueDirty(state: InteractiveState, preValue: T, curValue: T): void { - // @ts-ignore - preValue instanceof ReferResource && preValue._addReferCount(-1); + if (preValue instanceof ReferResource) { + preValue._addReferCount(-1); + } - // @ts-ignore - curValue instanceof ReferResource && curValue._addReferCount(1); + if (curValue instanceof ReferResource) { + curValue._addReferCount(1); + } if (this._finalState === state) { this._finalValue = curValue; this._updateValue(); } }This approach ensures that
preValue
andcurValue
are instances ofReferResource
before calling_addReferCount
, improving code safety and readability.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (preValue instanceof ReferResource) { preValue._addReferCount(-1); } if (curValue instanceof ReferResource) { curValue._addReferCount(1); }
151-162:
⚠️ Potential issueAdd a default case to handle unexpected states in
_getValueByState
The method
_getValueByState
lacks a default case in the switch statement. If an unexpectedInteractiveState
value is passed, the method may returnundefined
, leading to runtime errors.Add a default case to ensure the method always returns a valid value:
private _getValueByState(state: InteractiveState): T { switch (state) { case InteractiveState.Normal: return this.normal; case InteractiveState.Pressed: return this.pressed; case InteractiveState.Hover: return this.hover; case InteractiveState.Disable: return this.disabled; + default: + // Handle unexpected state, possibly by returning a default value or throwing an error + return this.normal; // or consider throwing an error } }This change ensures that your code handles all possible cases, enhancing robustness.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.private _getValueByState(state: InteractiveState): T { switch (state) { case InteractiveState.Normal: return this.normal; case InteractiveState.Pressed: return this.pressed; case InteractiveState.Hover: return this.hover; case InteractiveState.Disable: return this.disabled; default: // Handle unexpected state, possibly by returning a default value or throwing an error return this.normal; // or consider throwing an error } }
packages/ui/src/Utils.ts (2)
79-79: 🛠️ Refactor suggestion
Avoid assignments within logical expressions for better clarity.
In lines 79, 100, 122, 133, and 135, assignments are performed within logical
&&
expressions, which can be confusing and reduce readability.For example, at line 79:
replaced && (replaced._indexInCanvas = element._indexInCanvas);It's clearer to separate the assignment from the conditional check:
if (replaced) { replaced._indexInCanvas = element._indexInCanvas; }Apply similar changes to the other lines to enhance code clarity and maintainability.
Also applies to: 100-100, 122-122, 133-133, 135-135
🧰 Tools
🪛 Biome
[error] 79-79: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
18-18: 🛠️ Refactor suggestion
Minimize the use of
// @ts-ignore
to ensure type safety.The code contains multiple
// @ts-ignore
comments (lines 18, 21, 43, 62, 87, 108), which suppress TypeScript compiler errors. Overusing// @ts-ignore
can hide potential type issues and decrease code reliability. It's advisable to address the underlying type errors instead of suppressing them.Also applies to: 21-21, 43-43, 62-62, 87-87, 108-108
packages/core/src/input/pointer/emitter/PhysicsPointerEventEmitter.ts (1)
61-61: 🛠️ Refactor suggestion
Avoid assignments within expressions for better readability
On line 61, the assignment combines multiple variables in a single expression. This can reduce code clarity and make it harder to debug.
Consider separating the assignments to enhance readability:
-const entity = (this._pressedEntity = this._draggedEntity = this._enteredEntity); +this._pressedEntity = this._draggedEntity = this._enteredEntity; +const entity = this._pressedEntity;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this._pressedEntity = this._draggedEntity = this._enteredEntity; const entity = this._pressedEntity;
🧰 Tools
🪛 Biome
[error] 61-61: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 61-61: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/UIGroup.ts (3)
148-149:
⚠️ Potential issueRemove
@ts-ignore
and correctly override lifecycle methodsThe use of
@ts-ignore
when overriding_onEnableInScene()
and_onDisableInScene()
suggests type mismatches. Ensure that these methods are properly declared in the base class and that your overrides match the expected signatures without suppressing type errors.Consider updating your method names or checking the base class definitions to align with TypeScript's expectations.
Also applies to: 156-157
152-153: 🛠️ Refactor suggestion
Avoid accessing internal methods like
_dispatchModify
Accessing
this.entity._dispatchModify
directly may break encapsulation if_dispatchModify
is intended to be a private or internal method. Relying on internal APIs can lead to maintenance issues if the underlying implementation changes.Consider using a public method or event system provided by the
Entity
class to dispatch modifications.
90-91: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
; ensure_componentType
assignment is type-safeUsing
@ts-ignore
suppresses TypeScript compiler errors and can mask underlying issues. Assigning to_componentType
directly may not be safe if it's a protected or private member of the parent class. Consider providing a proper accessor or refactoring the class hierarchy to avoid bypassing TypeScript's type checking.Apply this diff to address the issue:
-// @ts-ignore this._componentType = ComponentType.UIGroup; + (this as any)._componentType = ComponentType.UIGroup;Or better, request an accessor from the base class if possible.
Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/interactive/UIInteractive.ts (3)
146-155: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
; properly handle method overriding in_onEnableInScene
.Using
@ts-ignore
suppresses TypeScript errors and may conceal potential issues. The method_onEnableInScene
is being overridden, but TypeScript likely flags an error due to access modifiers or method visibility in the base classScript
.Consider these options:
- Option 1: Update the base class
Script
to declare_onEnableInScene
asprotected
, allowing safe overriding.- Option 2: If modifying the base class is not feasible, create a new method in your subclass that does not conflict with the base class's private or internal methods.
- Option 3: Use TypeScript's
protected
orpublic
access modifiers to align with the intended visibility and override methods without suppressing errors.Removing
@ts-ignore
ensures that TypeScript can properly check your code for errors.
157-167: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
; ensure proper method overriding in_onDisableInScene
.Similar to the previous comment, suppressing TypeScript errors with
@ts-ignore
when overriding_onDisableInScene
can hide potential issues. Properly handling method overriding is essential for code safety and maintainability.Implement the same solutions as suggested for
_onEnableInScene
to address this issue.
105-109:
⚠️ Potential issueAdjust generic type constraint in
addTransition
method to support constructors with parameters.The current generic type constraint
T extends new () => Transition
may not accommodateTransition
subclasses that require constructor arguments. This can lead to issues when adding transitions that need parameters during instantiation.Apply this diff to adjust the generic type constraint and handle constructors with parameters:
- addTransition<T extends new () => Transition>(type: T): InstanceType<T> { - const transition = new type() as InstanceType<T>; + addTransition<T extends abstract new (...args: any[]) => Transition>(type: T, ...args: ConstructorParameters<T>): InstanceType<T> { + const transition = new type(...args) as InstanceType<T>; this._transitions.push(transition); transition._setState(this._state, true); return transition; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.addTransition<T extends abstract new (...args: any[]) => Transition>(type: T, ...args: ConstructorParameters<T>): InstanceType<T> { const transition = new type(...args) as InstanceType<T>; this._transitions.push(transition); transition._setState(this._state, true); return transition; }
packages/ui/src/input/UIPointerEventEmitter.ts (3)
222-222: 🛠️ Refactor suggestion
Avoid assignments within expressions for better readability
Similar to line 103, the assignment on line 222 uses an assignment within an expression, which can reduce code clarity.
Refactor the code as follows:
- let entity = (path[0] = element.entity); + path[0] = element.entity; + let entity = path[0];📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.path[0] = element.entity; let entity = path[0];
🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
89-89:
⚠️ Potential issueAvoid returning a value from a function with a
void
return typeThe
processRaycast
method is declared to returnvoid
, but it containsreturn null;
on line 89. Returning a value in avoid
function can lead to confusion and potential type errors.Apply this diff to correct the issue:
- return null; + return;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.return;
🧰 Tools
🪛 Biome
[error] 89-89: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
103-103: 🛠️ Refactor suggestion
Avoid assignments within expressions for better readability
Assigning multiple variables within an expression can make the code harder to read and maintain. The assignment on line 103 combines several assignments in one expression.
Refactor the code as follows to improve clarity:
- const element = (this._pressedElement = this._draggedElement = this._enteredElement); + this._pressedElement = this._draggedElement = this._enteredElement; + const element = this._pressedElement;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this._pressedElement = this._draggedElement = this._enteredElement; const element = this._pressedElement;
🧰 Tools
🪛 Biome
[error] 103-103: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 103-103: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/UIRenderer.ts (6)
134-135: 🛠️ Refactor suggestion
Eliminate the use of '@ts-ignore' by addressing type issues
The use of
// @ts-ignore
suppresses TypeScript's type checking, which may hide potential issues and reduce code maintainability. It's better to address the underlying type errors directly.Consider modifying the base class or adjusting type declarations to eliminate the need for
@ts-ignore
. For example, if_componentType
needs to be assigned, ensure it's properly declared in the base class or provide a setter method.
138-139: 🛠️ Refactor suggestion
Avoid accessing private properties directly
Accessing the
_onValueChanged
property directly may violate encapsulation principles, and using@ts-ignore
to suppress the error is not ideal. Consider providing a public or protected method to handle the color change callback.Refactor the
Color
class to expose a method or event for value changes, allowing you to subscribe without accessing private members.
179-182: 🛠️ Refactor suggestion
Replace '@ts-ignore' with proper type handling
The
@ts-ignore
directives here suppress errors related to_overrideUpdate
andscene._componentsManager
, possibly due to missing or incorrect type definitions.Ensure that
_overrideUpdate
andscene._componentsManager
are correctly defined and accessible. Update type declarations or class structures as necessary to eliminate the need for@ts-ignore
.
187-190: 🛠️ Refactor suggestion
Avoid suppressing TypeScript errors with '@ts-ignore'
Similar to previous comments, using
@ts-ignore
hides potential issues. Address the type errors directly for better code quality.Ensure that methods like
removeOnUpdateRenderers
are properly defined inscene._componentsManager
and that access levels permit their use.
215-215: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
Using assignments within expressions can make the code less readable and potentially cause confusion. Consider refactoring the code for better clarity and maintainability.
Apply this diff to improve readability:
- rootCanvas && (rootCanvas._hierarchyDirty = true); + if (rootCanvas) { + rootCanvas._hierarchyDirty = true; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (rootCanvas) { rootCanvas._hierarchyDirty = true; }
🧰 Tools
🪛 Biome
[error] 215-215: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
292-293: 🛠️ Refactor suggestion
Properly clean up event handlers without accessing private members
Setting
_onValueChanged
tonull
directly may lead to unexpected behavior, especially if_onValueChanged
is a private property.Provide a public method in the
Color
class to remove the event listener, ensuring encapsulation and proper cleanup.- //@ts-ignore - this._color._onValueChanged = null; + this._color.offValueChanged(this._onColorChange);Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/advanced/Image.ts (2)
123-124: 🛠️ Refactor suggestion
Avoid repeated use of
@ts-ignore
; properly handle TypeScript errorsThere are several instances where
@ts-ignore
is used to suppress TypeScript errors (lines 123, 129, 147, 150, and 284). This practice can hide potential bugs and should be avoided. Please consider properly typing the properties or methods, or refactoring the code to handle the type errors safely.Also applies to: 129-130, 147-147, 150-150, 284-285
27-27:
⚠️ Potential issueFix typo in variable name
_tempUnit8Array
The variable name
_tempUnit8Array
seems to have a typo. It should be_tempUint8Array
.Apply this diff to correct the typo:
- private static _tempUnit8Array: Uint8ClampedArray = new Uint8ClampedArray(4); + private static _tempUint8Array: Uint8ClampedArray = new Uint8ClampedArray(4);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.private static _tempUint8Array: Uint8ClampedArray = new Uint8ClampedArray(4);
packages/ui/src/component/advanced/Label.ts (5)
207-210: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
; resolve TypeScript errors insteadThe use of
@ts-ignore
suppresses TypeScript errors, which may hide underlying issues. It's better to address the root cause of the TypeScript errors to ensure type safety.Consider updating the code to resolve the TypeScript errors without suppressing them.
456-456: 🛠️ Refactor suggestion
Avoid assignments within conditional expressions
Assigning
maxY = Math.max(maxY, top)
within a conditional expression can reduce code clarity.Refactor the code as follows:
- i === firstLine && (maxY = Math.max(maxY, top)); + if (i === firstLine) { + maxY = Math.max(maxY, top); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (i === firstLine) { maxY = Math.max(maxY, top); }
🧰 Tools
🪛 Biome
[error] 456-456: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
458-458: 🛠️ Refactor suggestion
Avoid assignments within conditional expressions
Assigning
minX = Math.min(minX, left)
within a conditional expression can be confusing.Refactor the code as follows:
- j === firstRow && (minX = Math.min(minX, left)); + if (j === firstRow) { + minX = Math.min(minX, left); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (j === firstRow) { minX = Math.min(minX, left); }
🧰 Tools
🪛 Biome
[error] 458-458: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
228-228: 🛠️ Refactor suggestion
Avoid assignments within conditional expressions
Using an assignment within a conditional expression can be confusing and reduce code readability. It's clearer to separate the condition and the assignment.
Refactor the code as follows:
- this._subFont && (this._subFont = null); + if (this._subFont !== null) { + this._subFont = null; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (this._subFont !== null) { this._subFont = null; }
🧰 Tools
🪛 Biome
[error] 228-228: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
445-446: 🛠️ Refactor suggestion
Avoid assignments within conditional expressions
Assigning
firstRow = j
within a conditional expression can be misleading. It's better to use an explicitif
statement for clarity.Refactor the code as follows:
- firstRow < 0 && (firstRow = j); + if (firstRow < 0) { + firstRow = j; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (firstRow < 0) { firstRow = j; } const charRenderInfo = (charRenderInfos[renderElementCount++] = charRenderInfoPool.get());
🧰 Tools
🪛 Biome
[error] 445-445: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 446-446: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/UICanvas.ts (7)
406-408:
⚠️ Potential issueAdd missing
break
statement to prevent fallthrough in switch caseThe
case
forComponentType.UIRenderer
is missing abreak
statement, causing it to unintentionally fall through to the next caseComponentType.UIInteractive
. This can lead to unexpected behavior.Apply this diff to fix the issue:
switch (componentType) { case ComponentType.UIRenderer: elements[depth] = component as unknown as IGraphics; ++depth; + break; case ComponentType.UIInteractive: if ((component as unknown as IGroupAble)._isGroupDirty) { tempGroupAbleList[groupAbleCount++] = component as unknown as IGroupAble; }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case ComponentType.UIRenderer: elements[depth] = component as unknown as IGraphics; ++depth; break;
🧰 Tools
🪛 Biome
[error] 406-408: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
222-222: 🛠️ Refactor suggestion
Avoid assignments within variable declarations
Assigning a value within a variable declaration can reduce readability and may introduce unintended side effects. It's better to separate the assignments.
Apply the following diff to refactor the code:
- const renderElement = (this._renderElement = engine._renderElementPool.get()); + this._renderElement = engine._renderElementPool.get(); + const renderElement = this._renderElement;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this._renderElement = engine._renderElementPool.get(); const renderElement = this._renderElement;
🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
587-588:
⚠️ Potential issueAdd missing
break
statement to prevent fallthrough in switch caseThe
case
forCanvasRenderMode.ScreenSpaceOverlay
is missing abreak
statement, causing unintended fallthrough to the next case. This can lead to unexpected behavior.Apply this diff to fix the issue:
switch (preRealMode) { case CanvasRenderMode.ScreenSpaceOverlay: this._removeCanvasListener(); + break; case CanvasRenderMode.ScreenSpaceCamera: case CanvasRenderMode.WorldSpace: componentsManager.removeUICanvas(this, preRealMode === CanvasRenderMode.ScreenSpaceOverlay); break;
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 587-588: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
437-437: 🛠️ Refactor suggestion
Avoid assignments within logical expressions
Assigning within a logical expression can reduce code clarity and introduce side effects. It's better to separate the assignment from the expression.
Apply this diff to refactor the code:
- child.isActive && (depth = this._walk(child, elements, depth)); + if (child.isActive) { + depth = this._walk(child, elements, depth); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (child.isActive) { depth = this._walk(child, elements, depth); }
🧰 Tools
🪛 Biome
[error] 437-437: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
597-601:
⚠️ Potential issueAdd missing
break
statements to prevent fallthrough in switch casesThe
case
clauses forCanvasRenderMode.ScreenSpaceOverlay
andCanvasRenderMode.ScreenSpaceCamera
are missingbreak
statements, leading to unintended fallthrough. This can cause the code under subsequent cases to execute unintentionally.Apply this diff to fix the issue:
switch (curRealMode) { case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); + break; case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); + break; case CanvasRenderMode.WorldSpace: componentsManager.addUICanvas(this, curRealMode === CanvasRenderMode.ScreenSpaceOverlay); break;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); break; case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); break;
🧰 Tools
🪛 Biome
[error] 597-598: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
[error] 599-601: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
159-159: 🛠️ Refactor suggestion
Avoid assignments within logical expressions
Assigning values within logical expressions can be confusing and may lead to unintended side effects. It's clearer to separate the assignment from the expression.
Apply the following diff to refactor the code:
- this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay && - (this.scene._componentsManager._overlayCanvasesSortingFlag = true); + if (this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay) { + this.scene._componentsManager._overlayCanvasesSortingFlag = true; + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 159-159: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
236-238:
⚠️ Potential issueWrap variable declarations in switch cases with braces
Variables declared inside a
case
clause are accessible in subsequent cases due to JavaScript's scoping rules, which can lead to unexpected behavior. To prevent this, wrap thecase
block in braces to create a new scope.Apply this diff to fix the issue:
switch (mode) { case CanvasRenderMode.ScreenSpaceOverlay: + { const { min, max } = renderer.bounds; if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { continue; } + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 237-237: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/design/src/ui/IUIElement.ts (1)
1-4: 🛠️ Refactor suggestion
Consider revising the API design for better encapsulation
The current design exposes implementation details through underscore-prefixed members in a public interface, which is unconventional. Consider separating the public and internal APIs.
Here's a suggested refactor:
-export interface IUIElement { - _onUIUpdateIndex: number; - _onUpdate(): void; +/** Public interface for UI elements */ +export interface IUIElement { + /** Updates the UI element state */ + update(): void; +} + +/** Internal interface for UI system management */ +export interface IInternalUIElement extends IUIElement { + readonly updateIndex: number; +}This approach:
- Separates public and internal APIs
- Makes the update index readonly to prevent external modifications
- Provides a cleaner public interface for consumers
- Maintains type safety for internal system management
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** Public interface for UI elements */ export interface IUIElement { /** Updates the UI element state */ update(): void; } /** Internal interface for UI system management */ export interface IInternalUIElement extends IUIElement { readonly updateIndex: number; }
packages/design/src/ui/IUICanvas.ts (3)
1-7: 🛠️ Refactor suggestion
Improve type safety and interface design
The interface has several areas that could be improved:
- Replace
any
types with specific types to enhance type safety and IDE support- Avoid mixing public and private members in interfaces
- Add JSDoc documentation
Consider refactoring the interface like this:
+ /** + * Represents a UI canvas that can be rendered in the scene. + */ export interface IUICanvas { - entity: any; - sortOrder: number; - _canvasIndex: number; - _renderElement: any; - _prepareRender(renderContext: any): void; + /** The entity associated with this canvas */ + entity: Entity; // Replace with actual entity type + /** Determines the rendering order of the canvas */ + sortOrder: number; + /** Prepares the canvas for rendering */ + prepareRender(renderContext: RenderContext): void; // Replace with actual context type } + /** + * Internal implementation details for UI canvas. + * Consider moving these to a separate implementation class. + */ + interface IUICanvasImpl extends IUICanvas { + canvasIndex: number; + renderElement: RenderElement; // Replace with actual element type }Committable suggestion skipped: line range outside the PR's diff.
2-5: 💡 Codebase verification
Based on the search results, I can now generate the final response:
Use
Entity
andRenderElement
types instead ofany
The interface properties should be typed as follows:
entity
should be typed asEntity
since it's consistently used across the UI system_renderElement
should be typed asRenderElement
as shown by its usage in UI canvas implementationsThe types are available in the codebase and using them will provide better type safety and IDE support.
🔗 Analysis chain
Consider using strict typing for entity and renderElement
The use of
any
type reduces type safety. This could lead to runtime errors that TypeScript could otherwise catch during compilation.Let's check how these types are used in the codebase:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for entity type definitions and usage echo "Searching for Entity type definitions..." ast-grep --pattern 'class $_ implements Entity' ast-grep --pattern 'interface Entity' echo "Searching for RenderElement type definitions..." ast-grep --pattern 'class $_ implements RenderElement' ast-grep --pattern 'interface RenderElement'Length of output: 140
Script:
#!/bin/bash # Let's try a broader search to understand the usage of these properties echo "Searching for entity usage..." rg "entity:" -A 2 -B 2 echo "\nSearching for _renderElement usage..." rg "_renderElement:" -A 2 -B 2 echo "\nSearching for imports in the file..." rg "import.*from" "packages/design/src/ui/IUICanvas.ts" echo "\nChecking if this is an interface definition..." rg "interface.*IUICanvas" "packages/design/src/ui/IUICanvas.ts"Length of output: 36919
Script:
#!/bin/bash # Let's search for the actual types used in implementations of IUICanvas echo "Searching for UICanvas class implementations..." rg "class.*implements.*IUICanvas" echo "\nSearching for RenderElement type usage..." rg "RenderElement" -A 2 -B 2 echo "\nSearching for Entity type usage..." rg "Entity" "packages/design/src/ui/IUICanvas.ts"Length of output: 33934
6-6: 💡 Codebase verification
Change
any
type toRenderContext
from '@galacean/engine-core'The
renderContext
parameter in_prepareRender
should use theRenderContext
type from the core package, as evidenced by:
- The
RenderContext
class is defined inpackages/core/src/RenderPipeline/RenderContext.ts
- The method is used in
UIUtils.ts
with an instance of thisRenderContext
class- The interface is already importing other types from the core package
🔗 Analysis chain
Add type definition for renderContext parameter
The
renderContext
parameter should have a specific type rather thanany
to ensure type safety and provide better IDE support.Let's check for existing render context types:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for RenderContext type definitions echo "Searching for RenderContext type definitions..." ast-grep --pattern 'interface RenderContext' ast-grep --pattern 'class $_ implements RenderContext' # Search for usage patterns echo "Searching for renderContext parameter usage..." rg -t ts "renderContext" -A 2 -B 2Length of output: 208
Script:
#!/bin/bash # Let's search more broadly for render context related types and usages echo "Searching for render context related types..." rg -t ts "type.*RenderContext|interface.*RenderContext|class.*RenderContext" -B 1 -A 3 echo "\nSearching for _prepareRender implementations..." ast-grep --pattern 'class $_ { $$$ _prepareRender($_) { $$$ } $$$ }' echo "\nSearching for files importing IUICanvas..." rg -t ts "import.*IUICanvas"Length of output: 1137
Script:
#!/bin/bash # Let's examine the RenderContext class in detail echo "Examining RenderContext class..." cat packages/core/src/RenderPipeline/RenderContext.ts # Let's also check how _prepareRender is used with RenderContext echo "\nChecking _prepareRender usage with RenderContext..." rg -t ts "_prepareRender.*renderContext" -B 2 -A 2Length of output: 3941
packages/core/src/2d/text/ITextRenderer.ts (1)
7-7: 🛠️ Refactor suggestion
Reconsider the underscore prefix in method name
The method
_getSubFont
uses an underscore prefix, which typically indicates a private or internal member. However, in TypeScript interfaces, all members are inherently public. Consider removing the underscore prefix to better align with TypeScript conventions.- _getSubFont(): SubFont; + getSubFont(): SubFont;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.getSubFont(): SubFont;
packages/ui/src/interface/IElement.ts (2)
4-6: 🛠️ Refactor suggestion
Add JSDoc documentation and consider architectural improvements.
Add comprehensive JSDoc documentation to explain:
- The purpose and responsibility of the
IElement
interface- The relationship between elements, entities, and canvas
- Each property's purpose and usage
Consider architectural improvements:
- Could we reduce coupling with
Entity
by using a more abstract interface?- Should we add methods for lifecycle management and state updates?
Example documentation:
+/** + * Represents a UI element that can be rendered on a canvas. + * Provides core properties for managing the element's relationship + * with its underlying entity and canvas. + */ export interface IElement { + /** The engine entity associated with this UI element */ entity: Entity; + /** The canvas this element is rendered on */ readonly canvas: UICanvas;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** * Represents a UI element that can be rendered on a canvas. * Provides core properties for managing the element's relationship * with its underlying entity and canvas. */ export interface IElement { /** The engine entity associated with this UI element */ entity: Entity; /** The canvas this element is rendered on */ readonly canvas: UICanvas;
8-12: 💡 Codebase verification
⚠️ Potential issueBased on the results, I can now generate the final response:
Consider using existing event patterns instead of exposing internal implementation details
The codebase already has established patterns for event handling, including:
- Proper event type enums (e.g.,
PointerEventType
,XRInputEventType
)- Type-safe event parameters
- Clean event registration/unregistration mechanisms
Suggested refactor:
export enum CanvasEventType { Update = 1, Resize = 2, // Add other relevant types } export interface IElement { readonly entity: Entity; readonly canvas: UICanvas; // Public event API addEventListener(type: CanvasEventType, listener: (entity: Entity) => void): void; removeEventListener(type: CanvasEventType, listener: (entity: Entity) => void): void; dispose(): void; }This aligns with:
- Existing event patterns in the codebase
- Proper encapsulation of implementation details
- Type-safe event handling
- Clear cleanup through dispose pattern
🔗 Analysis chain
Critical: Refactor internal implementation details and improve type safety.
Several concerns with the current implementation:
Internal implementation details shouldn't be exposed in the interface:
_indexInCanvas
,_isCanvasDirty
,_canvasListeningEntities
should be implementation details- Consider moving these to an abstract class or internal interface
Type safety issues:
- Avoid using
any
type- The
flag
parameter should use an enum or const union typePotential memory management issues:
- Storing entity references in
_canvasListeningEntities
could lead to memory leaks- Need clear cleanup mechanism for entity listeners
Here's a suggested refactor:
/** Defines valid canvas event types */ export enum CanvasEventType { Update = 1, Resize = 2, // ... add other event types } /** Type for canvas event parameters */ export type CanvasEventParam = { type: CanvasEventType; data: unknown; }; export interface IElement { entity: Entity; readonly canvas: UICanvas; // Public methods instead of exposed internals addEventListener(type: CanvasEventType, listener: (event: CanvasEventParam) => void): void; removeEventListener(type: CanvasEventType, listener: (event: CanvasEventParam) => void): void; dispose(): void; }Let's verify the usage of internal properties across the codebase:
The initial results show extensive usage of these internal properties across multiple components. Let's verify:
- How the flag numbers are defined/used
- Whether proper cleanup mechanisms exist
- The implementation of the unregister mechanism
This will help determine if the suggested refactor is necessary or if the current implementation already has safeguards in place.
The previous results don't show clear event type definitions or proper cleanup patterns. Let's verify:
- If there are existing event type enums/constants
- The implementation of the registration mechanism
- Any type definitions for events
This will help determine if we need to introduce new event types or if there's an existing pattern we should follow.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for direct access to internal properties rg "_indexInCanvas|_isCanvasDirty|_canvasListeningEntities|_canvasListener" --type ts # Look for potential memory leak patterns with entity references ast-grep --pattern 'class $_ implements IElement { $$$ _canvasListeningEntities: Entity[] = []; $$$ }'Length of output: 4697
Script:
#!/bin/bash # Check for event type usage patterns rg "flag.*=.*\d+" --type ts -A 2 # Check for cleanup/disposal patterns ast-grep --pattern 'dispose() { $$$ _unRegisterListener($$$) $$$ }' # Check Utils._unRegisterListener implementation ast-grep --pattern '_unRegisterListener($$$) { $$$ }'Length of output: 2009
Script:
#!/bin/bash # Check for event flag definitions and constants rg "enum.*Event|const.*Event" --type ts -A 5 # Check Utils class implementation for event handling ast-grep --pattern 'class Utils { $$$ _registerListener($$$) { $$$ } $$$ }' # Look for event type definitions fd -e ts -x grep -l "type.*Event"Length of output: 29878
packages/ui/src/interface/IGroupAble.ts (1)
6-12: 🛠️ Refactor suggestion
Consider encapsulating internal state and improving type safety.
The interface mixes public and private members, which could lead to maintenance issues:
- Properties prefixed with underscore suggest internal implementation details but are still publicly accessible
- Optional properties (
_globalAlpha
,_globalInteractive
) lack documentation explaining when they might be undefinedConsider this alternative structure:
export interface IGroupAble extends IElement { readonly group: UIGroup; - _indexInGroup: number; - _groupListeningEntities: Entity[]; - _isGroupDirty: boolean; - _onUIUpdateIndex?: number; - _globalAlpha?: number; - _globalInteractive?: boolean; + /** @internal */ + private readonly _state: { + indexInGroup: number; + groupListeningEntities: Entity[]; + isGroupDirty: boolean; + onUIUpdateIndex?: number; + }; + + /** + * Global alpha value for the group. + * @description Controls the opacity of all elements in the group. + */ + globalAlpha: number; + + /** + * Global interactive state for the group. + * @description Determines if the group and its children can receive input events. + */ + globalInteractive: boolean;Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/interactive/transition/ScaleTransition.ts (1)
18-24: 🛠️ Refactor suggestion
Consider enhancing transition smoothness and adding validation.
A few suggestions for improvement:
- Consider using easing functions for smoother transitions
- Add validation for the weight parameter (should be between 0 and 1)
- Consider adding scale change event emission for animation tracking
protected override _updateCurrentValue(srcValue: number, destValue: number, weight: number): void { + if (weight < 0 || weight > 1) { + throw new Error('Weight must be between 0 and 1'); + } - this._currentValue = weight >= 1 ? destValue : (destValue - srcValue) * weight + srcValue; + // Apply easing function for smoother transition + const easedWeight = this._easeInOutQuad(weight); + this._currentValue = weight >= 1 ? destValue : (destValue - srcValue) * easedWeight + srcValue; } + private _easeInOutQuad(t: number): number { + return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; + } protected override _applyValue(value: number): void { this._target.entity.transform.setScale(value, value, value); + this._target.entity.emit('scaleChange', value); }Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/2d/index.ts (1)
3-5: 💡 Codebase verification
Classes are not implementing the ISpriteAssembler interface
The sprite assembler classes (
SimpleSpriteAssembler
,SlicedSpriteAssembler
, andTiledSpriteAssembler
) are not implementing theISpriteAssembler
interface that exists in the codebase. This could lead to type safety issues and inconsistent implementations.
./packages/core/src/2d/assembler/SimpleSpriteAssembler.ts
: Class declared without interface implementation./packages/core/src/2d/assembler/SlicedSpriteAssembler.ts
: Class declared without interface implementation./packages/core/src/2d/assembler/TiledSpriteAssembler.ts
: Class declared without interface implementation🔗 Analysis chain
Verify interface implementation in sprite assembler classes.
The new sprite assembler classes should implement the
ISpriteAssembler
interface.
Let me try a different approach to verify the interface implementation.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that sprite assembler classes implement ISpriteAssembler interface # Check implementation in each assembler class for file in SimpleSpriteAssembler SlicedSpriteAssembler TiledSpriteAssembler; do echo "Checking $file..." ast-grep --pattern 'class $file implements $interface { $$$ }' ./packages/core/src/2d/assembler/$file.ts doneLength of output: 851
Script:
#!/bin/bash # Search for the class declarations and their inheritance/implementation echo "=== SimpleSpriteAssembler ===" rg "class SimpleSpriteAssembler" ./packages/core/src/2d/assembler -A 2 echo -e "\n=== SlicedSpriteAssembler ===" rg "class SlicedSpriteAssembler" ./packages/core/src/2d/assembler -A 2 echo -e "\n=== TiledSpriteAssembler ===" rg "class TiledSpriteAssembler" ./packages/core/src/2d/assembler -A 2 # Also search for ISpriteAssembler interface definition echo -e "\n=== ISpriteAssembler interface ===" rg "interface ISpriteAssembler" ./packages/core/src/2d -A 2Length of output: 1861
packages/ui/src/component/interactive/transition/SpriteTransition.ts (1)
9-32: 🛠️ Refactor suggestion
Improve type safety and reduce code duplication in _destroy method.
Several concerns in the cleanup logic:
- Multiple
@ts-ignore
comments without explanation- Repetitive cleanup pattern for each state
- No error handling for reference counting
Consider refactoring to:
- override _destroy(): void { - if (this._normal) { - // @ts-ignore - this._normal._addReferCount(-1); - this._normal = null; - } - if (this._hover) { - // @ts-ignore - this._hover._addReferCount(-1); - this._hover = null; - } - if (this._pressed) { - // @ts-ignore - this._pressed._addReferCount(-1); - this._pressed = null; - } - if (this._disabled) { - // @ts-ignore - this._disabled._addReferCount(-1); - this._disabled = null; - } - this._initialValue = this._currentValue = this._finalValue = null; - this._target = null; + private _cleanupSprite(sprite: Sprite | null): void { + if (sprite) { + try { + (sprite as any)._addReferCount(-1); + } catch (e) { + console.warn('Failed to cleanup sprite reference:', e); + } + } + } + + override _destroy(): void { + const states = ['_normal', '_hover', '_pressed', '_disabled']; + states.forEach(state => { + this._cleanupSprite(this[state]); + this[state] = null; + }); + + this._initialValue = this._currentValue = this._finalValue = null; + this._target = null; }Also, please add a comment explaining why the type assertion is necessary for
_addReferCount
.Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/advanced/Button.ts (2)
32-35: 🛠️ Refactor suggestion
Improve cleanup implementation in onDestroy.
Similar to the removeClicked method, the cleanup could be more explicit and effective.
Consider this implementation:
override onDestroy(): void { super.onDestroy(); - this._listeners.findAndRemove((value) => (value.destroyed = true)); + // Clear all listeners + this._listeners.length = 0; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.override onDestroy(): void { super.onDestroy(); // Clear all listeners this._listeners.length = 0; }
🧰 Tools
🪛 Biome
[error] 34-34: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
11-21: 🛠️ Refactor suggestion
Improve listener management implementation.
The current implementation has two issues:
- Using assignment within the findAndRemove predicate reduces code clarity
- Marking listeners as destroyed without removing them could lead to memory leaks
Consider this cleaner implementation:
removeClicked(listener: (event: PointerEventData) => void): void { - this._listeners.findAndRemove((value) => (value.fn === listener ? (value.destroyed = true) : false)); + this._listeners.findAndRemove((value) => { + if (value.fn === listener) { + value.destroyed = true; + return true; // Remove the listener instead of just marking it + } + return false; + }); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.addClicked(listener: (event: PointerEventData) => void): void { this._listeners.push({ fn: listener }); } /** * Remove a listening function of click. * @param listener - The listening function */ removeClicked(listener: (event: PointerEventData) => void): void { this._listeners.findAndRemove((value) => { if (value.fn === listener) { value.destroyed = true; return true; // Remove the listener instead of just marking it } return false; }); }
🧰 Tools
🪛 Biome
[error] 20-20: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts (1)
15-15: 🛠️ Refactor suggestion
Add input validation for method parameters
The render method lacks validation for its input parameters. This could lead to runtime errors if invalid inputs are provided.
static render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { + if (!engine || !uiCanvases) { + throw new Error("Engine and uiCanvases parameters are required"); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.static render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { if (!engine || !uiCanvases) { throw new Error("Engine and uiCanvases parameters are required"); }
packages/ui/src/component/interactive/transition/ColorTransition.ts (2)
62-64:
⚠️ Potential issueAdd null check in _applyValue method.
The
_applyValue
method directly assigns to_target.color
without checking if_target
is defined.protected override _applyValue(value: Color): void { - this._target.color = value; + if (this._target) { + this._target.color = value; + } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected override _applyValue(value: Color): void { if (this._target) { this._target.color = value; } }
22-29: 💡 Codebase verification
⚠️ Potential issueRemove @ts-ignore comments by properly typing _onValueChanged property
The
Color
class already has the_onValueChanged
property properly defined as() => void
internally. The type safety issues can be resolved by:
- Removing the
@ts-ignore
comments- Ensuring the callback methods (
_onNormalValueChanged
, etc.) match the() => void
type signatureExample fix:
this._normal._onValueChanged = this._onNormalValueChanged; this._hover._onValueChanged = this._onHoverValueChanged; this._pressed._onValueChanged = this._onPressedValueChanged; this._disabled._onValueChanged = this._onDisabledValueChanged;🔗 Analysis chain
Address type safety issues instead of using @ts-ignore.
Multiple
@ts-ignore
comments suggest underlying type safety issues. Consider addressing these properly by:
- Adding proper type definitions for the
_onValueChanged
property- Extending the Color class if needed
Let's verify if the Color class can be extended or modified:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for Color class definition to understand its structure ast-grep --pattern 'class Color { $$$ }'Length of output: 44
Script:
#!/bin/bash # Let's try to find the Color class definition with ripgrep rg "class Color" -A 10 # Also search for any type definitions related to Color rg "type.*Color" --type=typescript # Search for Color-related files fd -e ts -e tsx "Color" # Search for _onValueChanged usage rg "_onValueChanged" -A 2Length of output: 38995
Script:
#!/bin/bash # Let's examine the Color class implementation to understand its type structure cat packages/math/src/Color.tsLength of output: 9743
packages/core/src/ComponentsDependencies.ts (2)
14-14: 🛠️ Refactor suggestion
Fix type and consider adding documentation.
- Use
boolean
instead ofBoolean
for the map value type- Consider adding JSDoc to document the purpose of this map
- Consider making the value type readonly since it's internal state
- static _inheritedMap = new Map<ComponentConstructor, Boolean>(); + /** Map tracking which components should be inherited by child entities */ + private static _inheritedMap = new Map<ComponentConstructor, boolean>();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** Map tracking which components should be inherited by child entities */ private static _inheritedMap = new Map<ComponentConstructor, boolean>();
🧰 Tools
🪛 Biome
[error] 14-14: Don't use 'Boolean' as a type.
Use lowercase primitives for consistency.
Safe fix: Use 'boolean' instead(lint/complexity/noBannedTypes)
97-101: 🛠️ Refactor suggestion
Enhance type safety and add validation.
The decorator should validate its usage and prevent potential memory leaks.
export function markAsInherited() { return function <T extends ComponentConstructor>(target: T): void { + if (!(target.prototype instanceof Component)) { + throw new Error("markAsInherited can only be used on Component classes"); + } + if (ComponentsDependencies._inheritedMap.has(target)) { + console.warn(`Component ${target.name} is already marked as inherited`); + return; + } ComponentsDependencies._inheritedMap.set(target, true); }; }Additionally, consider adding a cleanup mechanism to prevent memory leaks when components are removed or unregistered.
Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (2)
65-69: 🛠️ Refactor suggestion
Improve code readability by separating combined assignments
The combined assignment statements make the code harder to read and maintain. This was previously discussed but still needs to be addressed.
Apply this diff to improve readability:
- (row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); - row[3] = width - expectWidth * (1 - right) * widthScale; + row[0] = expectWidth * left * widthScale; + row[1] = fixedLeft * widthScale; + row[2] = fixedLeft * widthScale; + row[3] = width - expectWidth * (1 - right) * widthScale; - (column[0] = expectHeight * bottom * heightScale), (column[1] = column[2] = fixedBottom * heightScale); - column[3] = height - expectHeight * (1 - top) * heightScale; + column[0] = expectHeight * bottom * heightScale; + column[1] = fixedBottom * heightScale; + column[2] = fixedBottom * heightScale; + column[3] = height - expectHeight * (1 - top) * heightScale;Also applies to: 74-78
169-173: 🛠️ Refactor suggestion
Maintain consistent code style in getUVByLocalPosition
The same combined assignment pattern is used here. For consistency with the earlier suggestions, these should also be separated.
Apply this diff:
- (row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); - row[3] = width - expectWidth * (1 - right) * widthScale; + row[0] = expectWidth * left * widthScale; + row[1] = fixedLeft * widthScale; + row[2] = fixedLeft * widthScale; + row[3] = width - expectWidth * (1 - right) * widthScale;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.row[0] = expectWidth * left * widthScale; row[1] = fixedLeft * widthScale; row[2] = fixedLeft * widthScale; row[3] = width - expectWidth * (1 - right) * widthScale; } else { (row[0] = expectWidth * left), (row[1] = fixedLeft), (row[2] = width - fixedRight); row[3] = width - expectWidth * (1 - right);
packages/core/src/input/pointer/PointerManager.ts (2)
151-157:
⚠️ Potential issuePotential race condition in drag event processing
While the comment indicates that drag must be processed first, there's no synchronization mechanism to prevent race conditions when multiple emitters process events concurrently.
Consider adding a synchronization mechanism or using a queue to ensure proper event ordering:
- emitters.forEach((emitter) => { - emitter.processDrag(pointer); - }); + // Process drag events sequentially + for (const emitter of emitters) { + await emitter.processDrag(pointer); + }Committable suggestion skipped: line range outside the PR's diff.
231-262: 🛠️ Refactor suggestion
Add error boundaries for event processing
The event handling logic should include error handling to prevent cascading failures.
Consider wrapping the event processing in try-catch blocks:
switch (event.type) { case "pointerdown": { + try { const button = event.button; _downList.add(button); _downMap[button] = frameCount; pointer._downList.add(button); pointer._downMap[button] = frameCount; pointer._frameEvents |= PointerEventType.Down; pointer.phase = PointerPhase.Down; + } catch (error) { + console.error('Error processing pointer down event:', error); + pointer.phase = PointerPhase.Leave; // Fail safe + } break; }Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/ComponentsManager.ts (2)
185-188:
⚠️ Potential issueFix incorrect array reference in addOnUpdateUIElement
There's a critical bug where the method uses
this._onUpdateRenderers.length
instead ofthis._onUpdateUIElements.length
for the index.Apply this diff to fix the bug:
- element._onUIUpdateIndex = this._onUpdateRenderers.length; + element._onUIUpdateIndex = this._onUpdateUIElements.length;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.addOnUpdateUIElement(element: IUIElement): void { element._onUIUpdateIndex = this._onUpdateUIElements.length; this._onUpdateUIElements.add(element); }
272-276: 🛠️ Refactor suggestion
Align UI update pattern with other component updates
The
callUIOnUpdate
method doesn't follow the consistent pattern of managing indices during iteration. This could lead to stale indices.Apply this diff to align with other update methods:
callUIOnUpdate(deltaTime: number): void { - this._onUpdateUIElements.forEach((element: IUIElement) => { - element._onUpdate(); - }); + this._onUpdateUIElements.forEach( + (element: IUIElement) => { + element._onUpdate(); + }, + (element: IUIElement, index: number) => { + element._onUIUpdateIndex = index; + } + ); }Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/2d/text/TextUtils.ts (3)
98-103:
⚠️ Potential issueAdd parameter validation
The method should validate its numeric parameters to prevent potential rendering issues.
Add this validation at the start of the method:
static measureTextWithWrap( renderer: ITextRenderer, rendererWidth: number, rendererHeight: number, lineSpacing: number ): TextMetrics { + if (rendererWidth <= 0 || rendererHeight <= 0 || lineSpacing < 0) { + throw new Error('Invalid dimensions: width and height must be positive, lineSpacing must be non-negative'); + } const subFont = renderer._getSubFont();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.static measureTextWithWrap( renderer: ITextRenderer, rendererWidth: number, rendererHeight: number, lineSpacing: number ): TextMetrics { if (rendererWidth <= 0 || rendererHeight <= 0 || lineSpacing < 0) { throw new Error('Invalid dimensions: width and height must be positive, lineSpacing must be non-negative'); } const subFont = renderer._getSubFont();
98-103: 💡 Codebase verification
Tests need to be updated with new method parameters
The test file is using outdated method signatures for both
measureTextWithWrap
andmeasureTextWithoutWrap
. All production code has been properly updated, but the test file needs to be fixed. Here are the issues:
- In
measureTextWithWrap
test cases, the method is called without required parameters:rendererWidth
,rendererHeight
, andlineSpacing
- In
measureTextWithoutWrap
test cases, the method is called without required parameters:rendererHeight
andlineSpacing
The test renderer objects already have these values set up:
width
andheight
properties are set before each testlineSpacing
can be derived from the expectedlineHeight
assertions (27)🔗 Analysis chain
Verify all callers are updated with new parameters
The signature changes for both measurement methods require verification that all calling code has been updated accordingly.
Also applies to: 275-275
Let me analyze the search results:
For
measureTextWithWrap
, there are calls in:
packages/ui/src/component/advanced/Label.ts
- Correctly provides all parameters:this, rendererWidth, rendererHeight, this._lineSpacing
packages/core/src/2d/text/TextRenderer.ts
- Correctly provides all parameters (using_pixelsPerUnit
scaling)tests/src/core/2d/text/TextUtils.test.ts
- Test calls appear to be using old signatureFor
measureTextWithoutWrap
, there are calls in:
packages/ui/src/component/advanced/Label.ts
- Correctly provides all parameters:this, rendererHeight, this._lineSpacing
packages/core/src/2d/text/TextRenderer.ts
- Correctly provides all parameters (using_pixelsPerUnit
scaling)tests/src/core/2d/text/TextUtils.test.ts
- Test calls appear to be using old signatureLet me verify the test file to see if it's outdated:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for all calls to these methods echo "Searching for measureTextWithWrap calls:" rg -A 2 "measureTextWithWrap\(" --type ts echo -e "\nSearching for measureTextWithoutWrap calls:" rg -A 2 "measureTextWithoutWrap\(" --type tsLength of output: 7755
Script:
#!/bin/bash # Check the test file content around the method calls cat tests/src/core/2d/text/TextUtils.test.tsLength of output: 14944
275-275:
⚠️ Potential issueAdd parameter validation
Similar to measureTextWithWrap, this method should validate its numeric parameters.
Add this validation at the start of the method:
static measureTextWithoutWrap( renderer: ITextRenderer, rendererHeight: number, lineSpacing: number ): TextMetrics { + if (rendererHeight <= 0 || lineSpacing < 0) { + throw new Error('Invalid dimensions: height must be positive, lineSpacing must be non-negative'); + } const subFont = renderer._getSubFont();Committable suggestion skipped: line range outside the PR's diff.
packages/core/src/2d/assembler/TiledSpriteAssembler.ts (2)
81-86:
⚠️ Potential issueReplace global isNaN with Number.isNaN
Using the global
isNaN
is unsafe as it performs type coercion. UseNumber.isNaN
instead for more predictable behavior.- if (isNaN(cUV.get(doubleJ)) || isNaN(cUV.get(doubleJ + 1))) { + if (Number.isNaN(cUV.get(doubleJ)) || Number.isNaN(cUV.get(doubleJ + 1))) { continue; } for (let i = 0; i < rowLength; i++) { const doubleI = i << 1; - if (isNaN(rUV.get(doubleI)) || isNaN(rUV.get(doubleI + 1))) { + if (Number.isNaN(rUV.get(doubleI)) || Number.isNaN(rUV.get(doubleI + 1))) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (Number.isNaN(cUV.get(doubleJ)) || Number.isNaN(cUV.get(doubleJ + 1))) { continue; } for (let i = 0; i < rowLength; i++) { const doubleI = i << 1; if (Number.isNaN(rUV.get(doubleI)) || Number.isNaN(rUV.get(doubleI + 1))) {
🧰 Tools
🪛 Biome
[error] 81-81: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 81-81: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 86-86: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 86-86: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
422-444:
⚠️ Potential issueAdd block scoping to switch cases
Variables declared in switch cases can leak into other cases. Wrap the declarations in blocks to prevent this.
switch (rType) { case TiledType.Compressed: { - const scale = width / fixedLR; - rPos.add(expectWidth * left * scale), rPos.add(fixedL * scale); - rPos.add(width - expectWidth * (1 - right) * scale); - rUV.add(spriteUV0.x), rUV.add(spriteUV1.x), rUV.add(spriteUV2.x), rUV.add(spriteUV3.x); + { + const scale = width / fixedLR; + rPos.add(expectWidth * left * scale), rPos.add(fixedL * scale); + rPos.add(width - expectWidth * (1 - right) * scale); + rUV.add(spriteUV0.x), rUV.add(spriteUV1.x), rUV.add(spriteUV2.x), rUV.add(spriteUV3.x); + } break; } case TiledType.WithTiled: { - const uv1 = spriteUV1.x; - const uv2 = spriteUV2.x; - const repeatWidth = (width - fixedLR) / rTiledCount; + { + const uv1 = spriteUV1.x; + const uv2 = spriteUV2.x; + const repeatWidth = (width - fixedLR) / rTiledCount;Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 422-422: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 434-434: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 435-435: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 436-436: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/core/src/Entity.ts (1)
613-622: 🛠️ Refactor suggestion
Fix assignment in expression
The modification listener management is good, but the assignment in expression should be split for better readability.
- (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); + if (!this._updateFlagManager) { + this._updateFlagManager = new UpdateFlagManager(); + } + this._updateFlagManager.addListener(onChange);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements._registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { if (!this._updateFlagManager) { this._updateFlagManager = new UpdateFlagManager(); } this._updateFlagManager.addListener(onChange); } /** * @internal */ _unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { this._updateFlagManager?.removeListener(onChange); }
🧰 Tools
🪛 Biome
[error] 614-614: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
🛑 Comments failed to post (76)
packages/ui/src/component/UITransform.ts (1)
32-41:
⚠️ Potential issueAvoid using
@ts-ignore
to suppress TypeScript errorsThe use of
@ts-ignore
comments in the constructor to access private or protected properties can lead to maintenance issues and hides potential type safety problems. Specifically, accessing:
entity._transform.destroy();
- Setting
entity._transform = this;
- Assigning to
entity._inverseWorldMatFlag
- Binding
_onValueChanged
callbacks for_size
and_pivot
This practice violates encapsulation principles and may cause unexpected behavior if internal implementations of these classes change.
Consider the following actions:
- Expose necessary functionality through public APIs: Modify the
Entity
,Transform
, andVector2
classes to provide public methods or properties that allow the required interactions.- Refactor the design: Reevaluate the class interactions to ensure that they adhere to object-oriented design principles without needing to access internal properties.
This approach maintains type safety and improves code maintainability.
packages/ui/src/component/interactive/transition/Transition.ts (4)
25-67: 🛠️ Refactor suggestion
Avoid comparing object references in setters; use appropriate equality checks instead
In the setters for
normal
,pressed
,hover
, anddisabled
(lines 25-67), comparing objects using!==
may not accurately detect changes for object types likeSprite
orColor
, as it checks reference equality rather than value equality. This could lead to missed updates when the object's properties change but the reference remains the same. Consider implementing proper equality checks to ensure changes are detected correctly.You can modify the comparison to use a deep equality check or implement an equality method for
TransitionValueType
. For example:-if (preNormal !== value) { +if (!this._areValuesEqual(preNormal, value)) {And add a helper method
_areValuesEqual
:private _areValuesEqual(a: T, b: T): boolean { if (typeof a === 'number' && typeof b === 'number') { return a === b; } else if (a instanceof Color && b instanceof Color) { return a.equals(b); } else if (a instanceof Sprite && b instanceof Sprite) { return a === b; // Update this logic if necessary to compare Sprites } return false; }
151-162:
⚠️ Potential issueAdd a default case in '_getValueByState' to prevent returning undefined
In the
_getValueByState
method (lines 151-162), there is no default case in the switch statement. If an unexpectedInteractiveState
value is passed, the method may returnundefined
, leading to potential runtime errors when the value is used elsewhere. To enhance robustness, add a default case to handle unexpected states.Apply the following modification:
private _getValueByState(state: InteractiveState): T { switch (state) { case InteractiveState.Normal: return this.normal; case InteractiveState.Pressed: return this.pressed; case InteractiveState.Hover: return this.hover; case InteractiveState.Disable: return this.disabled; + default: + return this.normal; // Or handle appropriately based on your application's logic } }Alternatively, you could throw an error for unrecognized states to catch issues during development:
default: throw new Error(`Unhandled InteractiveState: ${state}`);
134-143: 🛠️ Refactor suggestion
Avoid using '// @ts-ignore'; properly handle TypeScript type checks
In the
_onStateValueDirty
method (lines 134-143), suppressing TypeScript errors with// @ts-ignore
is discouraged, as it can conceal underlying issues. Instead, use type assertions or type guards to handle cases wherepreValue
andcurValue
might be instances ofReferResource
.Modify the code to include type checks without suppressing TypeScript errors:
-// @ts-ignore -preValue instanceof ReferResource && preValue._addReferCount(-1); -// @ts-ignore -curValue instanceof ReferResource && curValue._addReferCount(1); +if (preValue instanceof ReferResource) { + preValue._addReferCount(-1); +} +if (curValue instanceof ReferResource) { + curValue._addReferCount(1); +}Ensure that
TransitionValueType
is correctly defined to include types that may extendReferResource
, or adjust the type casting as needed.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected _onStateValueDirty(state: InteractiveState, preValue: T, curValue: T): void { if (preValue instanceof ReferResource) { preValue._addReferCount(-1); } if (curValue instanceof ReferResource) { curValue._addReferCount(1); } if (this._finalState === state) { this._finalValue = curValue; this._updateValue(); } }
85-94:
⚠️ Potential issueFix potential division by zero in duration setter
In the
set duration(value: number)
method (lines 85-94), ifpreDuration
is zero and_countDown > 0
, the calculationthis._countDown / preDuration
will result in a division by zero error. This can cause runtime exceptions and unexpected behavior. Ensure thatpreDuration
is not zero before performing the division to prevent this issue.Apply the following fix:
const preDuration = this._duration; if (preDuration !== value) { this._duration = value; if (this._countDown > 0) { + if (preDuration !== 0) { this._countDown = value * (1 - this._countDown / preDuration); + } else { + this._countDown = value; + } this._updateValue(); } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (value < 0) value = 0; const preDuration = this._duration; if (preDuration !== value) { this._duration = value; if (this._countDown > 0) { if (preDuration !== 0) { this._countDown = value * (1 - this._countDown / preDuration); } else { this._countDown = value; } this._updateValue(); } } }
packages/ui/src/Utils.ts (5)
133-133:
⚠️ Potential issueAvoid assignments within expressions for better clarity
For improved readability, refactor the assignment to be explicit.
Apply this diff:
- replaced && (replaced._indexInCanvas = element._indexInCanvas); + if (replaced) { + replaced._indexInCanvas = element._indexInCanvas; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (replaced) { replaced._indexInCanvas = element._indexInCanvas; }
🧰 Tools
🪛 Biome
[error] 133-133: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
100-100:
⚠️ Potential issueAvoid assignments within expressions for better clarity
Using assignments within expressions can reduce code readability. Refactor the assignment to make it explicit.
Apply this diff:
- replaced && (replaced._indexInGroup = element._indexInGroup); + if (replaced) { + replaced._indexInGroup = element._indexInGroup; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (replaced) { replaced._indexInGroup = element._indexInGroup; }
🧰 Tools
🪛 Biome
[error] 100-100: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
122-122:
⚠️ Potential issueAvoid assignments within expressions for better clarity
Assignments within expressions can be confusing. It's better to make the assignment explicit.
Apply this diff:
- replaced && (replaced._indexInGroup = element._indexInGroup); + if (replaced) { + replaced._indexInGroup = element._indexInGroup; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (replaced) { replaced._indexInGroup = element._indexInGroup; }
🧰 Tools
🪛 Biome
[error] 122-122: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
135-135:
⚠️ Potential issueAvoid assignments within expressions for better clarity
Making assignments explicit enhances code clarity.
Apply this diff:
- isGraphics && (preCanvas._hierarchyDirty = true); + if (isGraphics) { + preCanvas._hierarchyDirty = true; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (isGraphics) { preCanvas._hierarchyDirty = true; }
🧰 Tools
🪛 Biome
[error] 135-135: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
79-79:
⚠️ Potential issueAvoid assignments within expressions for better clarity
Using assignments within expressions can make the code harder to read and may lead to confusion. It's recommended to refactor the assignment to make it more explicit.
Apply this diff to improve clarity:
- replaced && (replaced._indexInCanvas = element._indexInCanvas); + if (replaced) { + replaced._indexInCanvas = element._indexInCanvas; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (replaced) { replaced._indexInCanvas = element._indexInCanvas; }
🧰 Tools
🪛 Biome
[error] 79-79: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/input/pointer/emitter/PhysicsPointerEventEmitter.ts (1)
61-61: 🛠️ Refactor suggestion
Avoid assignments within expressions to improve readability
Assigning variables within an expression can reduce code readability and may introduce unintended side effects. It's better to separate assignments into individual statements.
Apply this diff to improve clarity:
- const entity = (this._pressedEntity = this._draggedEntity = this._enteredEntity); + this._pressedEntity = this._draggedEntity = this._enteredEntity; + const entity = this._pressedEntity;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this._pressedEntity = this._draggedEntity = this._enteredEntity; const entity = this._pressedEntity;
🧰 Tools
🪛 Biome
[error] 61-61: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 61-61: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/UIGroup.ts (4)
90-94:
⚠️ Potential issueAvoid using
@ts-ignore
; ensure TypeScript compatibility instead.Using
@ts-ignore
suppresses TypeScript errors, potentially hiding real issues. Theoverride
modifier and direct assignment to_componentType
might not be recognized in your current TypeScript configuration. Ensure that your project uses a compatible TypeScript version (4.3 or higher) and adjust access modifiers or class structures to avoid the need for@ts-ignore
.
148-149:
⚠️ Potential issueRemove
@ts-ignore
and verify method overriding correctly.The use of
@ts-ignore
before theoverride
keyword suppresses potential TypeScript errors. Ensure that the base classComponent
has the method_onEnableInScene
and that you're correctly overriding it. Updating your TypeScript version or compiler options might resolve this without ignoring errors.
184-190:
⚠️ Potential issueUse bitwise operations when checking flags in
_canvasListener
.Similar to the previous issue, the flag check should use bitwise operations to handle multiple flags correctly.
Apply this diff:
- if (flag === EntityModifyFlags.Parent) { + if ((flag & EntityModifyFlags.Parent) !== 0) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements._canvasListener(flag: number): void { if (this._isCanvasDirty) return; if ((flag & EntityModifyFlags.Parent) !== 0) { Utils._onCanvasDirty(this, this._canvas); Utils._onGroupDirty(this, this._group); } }
174-178:
⚠️ Potential issueUse bitwise operations when checking flags in
_groupListener
.The current flag check uses strict equality, which may fail if multiple flags are set simultaneously. Modify the condition to use bitwise operations to correctly detect flag combinations.
Apply this diff to fix the issue:
- if (flag === EntityModifyFlags.Parent || flag === EntityUIModifyFlags.UIGroupEnableInScene) { + if ((flag & (EntityModifyFlags.Parent | EntityUIModifyFlags.UIGroupEnableInScene)) !== 0) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (this._isGroupDirty) return; if ((flag & (EntityModifyFlags.Parent | EntityUIModifyFlags.UIGroupEnableInScene)) !== 0) { Utils._onGroupDirty(this, this._group); } }
packages/ui/src/component/interactive/UIInteractive.ts (4)
151-152:
⚠️ Potential issueAvoid accessing private members of other classes
Accessing
_componentsManager
fromthis.scene
in lines 151 and 162 is likely accessing a private member of theScene
class. Accessing private properties breaks encapsulation and can lead to maintenance issues.Consider using public methods or properties provided by
Scene
to register and unregister UI elements. If such methods do not exist, it may be necessary to update theScene
class to expose the required functionality.Also applies to: 162-163
176-183: 🛠️ Refactor suggestion
Ensure
_onUpdate
properly updates interaction stateCurrently,
_onUpdate
only calls_updateGlobalInteractive()
. To ensure that interaction states are consistently updated, consider also calling_updateState(false)
within this method.Apply this change:
_onUpdate(): void { this._updateGlobalInteractive(); + this._updateState(false); }
This guarantees that the state reflects any changes in interactivity each frame.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** * @internal */ _onUpdate(): void { this._updateGlobalInteractive(); this._updateState(false); } /**
146-149:
⚠️ Potential issueAddress the use of multiple
@ts-ignore
commentsThe repeated use of
// @ts-ignore
suggests that TypeScript errors are being suppressed. This can hide potential issues and reduce type safety. Investigate and resolve the underlying TypeScript errors instead of suppressing them.For example, if the
_onEnableInScene
method is not defined in the parent class or has a different access modifier, consider updating the method access or properly extending the class.- // @ts-ignore override _onEnableInScene(): void { - // @ts-ignore super._onEnableInScene(); // ... }Ensure that
_onEnableInScene
is correctly declared in the parent classScript
and that it can be overridden.Committable suggestion skipped: line range outside the PR's diff.
104-109: 🛠️ Refactor suggestion
Refine generic type constraints in
addTransition
methodIn the
addTransition
method, the generic typeT
is constrained asnew () => Transition
, but this may not correctly capture subclasses ofTransition
with their own constructor signatures.Modify the generic constraint to accept any constructor of a
Transition
subclass:-addTransition<T extends new () => Transition>(type: T): InstanceType<T> { +addTransition<T extends Transition>(type: { new (...args: any[]): T }): T { const transition = new type(); // ... }Similarly, update the return type to
T
for clarity.Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/input/UIPointerEventEmitter.ts (8)
103-103:
⚠️ Potential issueAvoid assignment within an expression for clarity
At line 103, there is an assignment within an expression:
const element = (this._pressedElement = this._draggedElement = this._enteredElement);This can make the code harder to read and understand. It's better to separate the assignments for improved readability.
Apply this diff to refactor the code:
- const element = (this._pressedElement = this._draggedElement = this._enteredElement); + this._pressedElement = this._draggedElement = this._enteredElement; + const element = this._pressedElement;🧰 Tools
🪛 Biome
[error] 103-103: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 103-103: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
222-222:
⚠️ Potential issueAvoid assignment within an expression for clarity
At line 222, there's an assignment within an expression:
let entity = (path[0] = element.entity);Assigning within an expression can reduce code readability. Separating the assignments makes the code clearer.
Apply this diff to refactor the code:
- let entity = (path[0] = element.entity); + path[0] = element.entity; + let entity = path[0];🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
35-35: 🛠️ Refactor suggestion
Accessing private properties using
@ts-ignore
is discouragedAt line 35:
// @ts-ignore const componentsManager = scene._componentsManager;Using
@ts-ignore
to access private members bypasses TypeScript's type checking, which can lead to maintenance issues and potential runtime errors.Consider exposing
componentsManager
through a public getter or method in theScene
class. This way, you can access it without ignoring TypeScript's checks.
205-212: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
to access private membersAt lines 205-212:
// @ts-ignore entity._scripts.forEach( (script: Script) => { script[methods]?.(eventData); }, (script: Script, index: number) => { // @ts-ignore script._entityScriptsIndex = index; } );Using
@ts-ignore
to access_scripts
and_entityScriptsIndex
can lead to fragile code. It bypasses the safety offered by TypeScript's access modifiers.Consider adding public methods or accessors to interact with the scripts. For example, the
Entity
class could provide a method to get its scripts, andScript
could manage its index internally.
225-229: 🛠️ Refactor suggestion
Ensure proper access to component properties
At lines 225-229, you are accessing
_componentType
and_isRootCanvas
:// @ts-ignore if (element._componentType === ComponentType.UICanvas && element._isRootCanvas) { path.length = 1; return path; } else {Accessing private or protected properties with
@ts-ignore
should be avoided as it circumvents TypeScript's type safety.Propose exposing necessary properties or methods in the
Component
orUICanvas
classes to access this information without using@ts-ignore
.
217-235:
⚠️ Potential issuePotential infinite loop in
_composedPath
methodIn the
_composedPath
method, you have a loop that depends onentity.parent
:for (; i < UIPointerEventEmitter._MAX_PATH_DEPTH && !!entity && entity !== rootEntity; i++) { entity = path[i] = entity.parent; }There is a risk of entering an infinite loop if there is a cycle in the entity's parent hierarchy or if
rootEntity
is not correctly identified.Consider adding safeguards to detect cycles or ensure that
rootEntity
is correctly set.🧰 Tools
🪛 Biome
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
89-89: 💡 Codebase verification
Return statement is inconsistent with void return type
The
processRaycast
method is declared with a void return type but includes areturn null
statement. This is unnecessary since:
- The method already has multiple
return
statements without values- Void functions should not return values in TypeScript
Remove the
return null
statement at line 89 as it's both unnecessary and inconsistent with the method's signature.🔗 Analysis chain
Verify: Consistency of the return type with method signature
As per the static analysis hint, the function
processRaycast
is expected to returnvoid
, but it includes areturn null;
statement at line 89.Run the following script to check for any other instances where
void
functions return a value:🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Find methods declared as void that return a value ast-grep 'function $_(...) : void { $$$; return $_; $$$ }' --lang typescriptLength of output: 223
Script:
#!/bin/bash # Let's first check the method signature and implementation rg -U "processRaycast.*void.*\{[\s\S]*?return null" packages/ui/src/input/UIPointerEventEmitter.ts # Also check the entire file content for better context cat packages/ui/src/input/UIPointerEventEmitter.tsLength of output: 10852
🧰 Tools
🪛 Biome
[error] 89-89: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
26-89:
⚠️ Potential issueRemove the
return
statement in avoid
functionThe
processRaycast
method is declared with a return type ofvoid
, but it includes areturn null;
statement at line 89. Returning a value from avoid
function can cause confusion and may lead to unexpected behavior.Apply this diff to correct the issue:
89 - return null; + return;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.override processRaycast(scenes: readonly Scene[], pointer: Pointer): void { const { _tempRay: ray } = PointerEventEmitter; const hitResult = this._hitResult; const { position } = pointer; const { x, y } = position; for (let i = scenes.length - 1; i >= 0; i--) { const scene = scenes[i]; if (!scene.isActive || scene.destroyed) continue; // @ts-ignore const componentsManager = scene._componentsManager; /** Overlay Canvas */ let canvasElements = componentsManager._overlayCanvases; ray.origin.set(position.x, position.y, 1); ray.direction.set(0, 0, -1); for (let j = canvasElements.length - 1; j >= 0; j--) { if (canvasElements.get(j).raycast(ray, hitResult)) { this._updateRaycast(<Component>hitResult.component, pointer); return; } } const cameras = componentsManager._activeCameras; for (let j = cameras.length - 1; j >= 0; j--) { const camera = cameras.get(j); if (camera.renderTarget) continue; const { pixelViewport } = camera; if ( x < pixelViewport.x || y < pixelViewport.y || x > pixelViewport.x + pixelViewport.width || y > pixelViewport.y + pixelViewport.height ) { continue; } camera.screenPointToRay(pointer.position, ray); /** Other canvases */ const cameraPosition = camera.entity.transform.position; /** Sort by rendering order */ canvasElements = componentsManager._canvases; for (let k = 0, n = canvasElements.length; k < n; k++) { canvasElements.get(k)._updateSortDistance(cameraPosition); } canvasElements.sort((a, b) => a.sortOrder - b.sortOrder || a._sortDistance - b._sortDistance); for (let k = 0, n = canvasElements.length; k < n; k++) { canvasElements.get(k)._canvasIndex = k; } const farClipPlane = camera.farClipPlane; /** Post-rendering first detection */ for (let k = 0, n = canvasElements.length; k < n; k++) { const canvas = canvasElements.get(k); if (canvas.renderCamera !== camera) continue; if (canvas.raycast(ray, hitResult, farClipPlane)) { this._updateRaycast(<Component>hitResult.component, pointer); return; } } if (camera.clearFlags & CameraClearFlags.Color) { this._updateRaycast(null); return; } } } return;
🧰 Tools
🪛 Biome
[error] 89-89: The function should not return a value because its return type is void.
The function is here:
'void' signals the absence of value. The returned value is likely to be ignored by the caller.
(lint/correctness/noVoidTypeReturn)
packages/ui/src/component/UIRenderer.ts (5)
134-135: 🛠️ Refactor suggestion
Avoid suppressing TypeScript errors with
// @ts-ignore
The use of
// @ts-ignore
suppresses TypeScript compiler errors, which can mask underlying issues in the code. It's important to address the root cause of the TypeScript error to ensure type safety and maintainability.Consider adding a proper type declaration for
_componentType
:// Before the constructor, add the property declaration protected _componentType: ComponentType;This will eliminate the need for
// @ts-ignore
and make the code more robust.
139-139: 🛠️ Refactor suggestion
Avoid direct access to internal properties
Directly accessing internal properties like
_onValueChanged
can break encapsulation and lead to maintenance challenges. It's better to use public methods or properties provided by the class.Consider adding a method to
Color
class to handle the value change subscription:// In Color class public set onValueChanged(callback: () => void) { this._onValueChanged = callback; }And then update your code:
- //@ts-ignore - this._color._onValueChanged = this._onColorChange; + this._color.onValueChanged = this._onColorChange;
292-292: 🛠️ Refactor suggestion
Avoid direct assignment to internal properties
Setting internal properties like
_onValueChanged
tonull
directly can cause unexpected behavior. Provide a proper method to unsubscribe or dispose of event handlers.Modify the
Color
class to include an unsubscribe method:// In Color class public clearOnValueChanged(): void { this._onValueChanged = null; }And update your code accordingly:
- //@ts-ignore - this._color._onValueChanged = null; + this._color.clearOnValueChanged();
173-175: 🛠️ Refactor suggestion
Resolve TypeScript issues instead of using
// @ts-ignore
Using
// @ts-ignore
bypasses TypeScript's type checking, which can lead to runtime errors. It's crucial to address the type incompatibilities to maintain type safety.Investigate the types of
this.shaderData._macroCollection
andthis._globalShaderMacro
to ensure they match the expected parameters ofShaderMacroCollection.unionCollection
. Update the type definitions if necessary to align with the method's requirements.
214-215:
⚠️ Potential issueRefactor assignment in expression for clarity
Using an assignment within a logical expression can be confusing and may lead to unintended side effects. At line 215, the assignment
_hierarchyDirty = true
is made within a logical AND operation. This can be refactored for better readability and to follow best practices.Apply this diff to improve the code clarity:
const rootCanvas = this._canvas; - rootCanvas && (rootCanvas._hierarchyDirty = true); + if (rootCanvas) { + rootCanvas._hierarchyDirty = true; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const rootCanvas = this._canvas; if (rootCanvas) { rootCanvas._hierarchyDirty = true; }
🧰 Tools
🪛 Biome
[error] 215-215: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/ui/src/component/advanced/Image.ts (2)
150-150:
⚠️ Potential issueUndefined method
_onColorChange
The method
_onColorChange
is referenced but not defined in theImage
class. This will cause a runtime error when_onColorChange
is called.Consider adding the
_onColorChange
method to handle updates when the color changes. Here's a possible implementation:private _onColorChange(): void { this._dirtyUpdateFlag |= UIRendererUpdateFlags.Color; }
146-147: 🛠️ Refactor suggestion
Avoid using
@ts-ignore
by addressing TypeScript type issuesUsing
@ts-ignore
suppresses TypeScript errors, potentially hiding bugs and undermining type safety. It's better to resolve the underlying type issues to ensure code reliability.Review the following instances where
@ts-ignore
is used:
- Lines 123-125 and 129-131: Accessing
_updateFlagManager
onSprite
.- Lines 146-147 and 218-219: Accessing
_engine._basicResources.uiDefaultMaterial
.- Lines 149-150: Assigning to
this._color._onValueChanged
.Consider the following actions:
- Expose necessary properties or methods: If properties like
_updateFlagManager
and_basicResources
are needed externally, provide public getters or methods in their respective classes.- Adjust TypeScript definitions: Ensure all accessed properties and methods are correctly typed and accessible according to their visibility (public, protected, private).
- Refactor code: Modify the design to eliminate the need for accessing internal properties or using
@ts-ignore
.Also applies to: 149-150, 123-125, 129-131, 218-219
packages/ui/src/component/advanced/Label.ts (6)
228-228: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
Using assignments within expressions can make the code harder to read and maintain. Consider refactoring the assignment for better readability.
- this._subFont && (this._subFont = null); + if (this._subFont) { + this._subFont = null; + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (this._subFont) { this._subFont = null; }
🧰 Tools
🪛 Biome
[error] 228-228: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
458-458: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
For better code readability, avoid assigning values within expressions. Refactor to use an
if
statement.- j === firstRow && (minX = Math.min(minX, left)); + if (j === firstRow) { + minX = Math.min(minX, left); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (j === firstRow) { minX = Math.min(minX, left); }
🧰 Tools
🪛 Biome
[error] 458-458: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
456-456: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
Assignments within expressions can reduce code clarity. Use an explicit conditional statement instead.
- i === firstLine && (maxY = Math.max(maxY, top)); + if (i === firstLine) { + maxY = Math.max(maxY, top); + }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (i === firstLine) { maxY = Math.max(maxY, top); }
🧰 Tools
🪛 Biome
[error] 456-456: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
445-446: 🛠️ Refactor suggestion
Avoid assignments within expressions for clarity
The assignment inside the logical expression can be confusing. Refactor to an explicit
if
statement for improved readability.if (charInfo.h > 0) { - firstRow < 0 && (firstRow = j); + if (firstRow < 0) { + firstRow = j; + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 445-445: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 446-446: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
231-234: 🛠️ Refactor suggestion
Avoid suppressing TypeScript errors with
@ts-ignore
Suppressing TypeScript checks can mask potential issues. Refactor the code to address the type errors without using
@ts-ignore
.- // @ts-ignore override _cloneTo(target: Label, srcRoot: Entity, targetRoot: Entity): void { - // @ts-ignore super._cloneTo(target, srcRoot, targetRoot); target.font = this._font; }Ensure that method overrides correctly match the base class signatures and that types are properly defined.
Committable suggestion skipped: line range outside the PR's diff.
207-210: 🛠️ Refactor suggestion
Avoid suppressing TypeScript errors with
@ts-ignore
Using
@ts-ignore
may hide underlying issues and lead to maintenance challenges. Consider resolving the TypeScript errors directly or adjusting the access modifiers.- // @ts-ignore this.font = engine._textDefaultFont; - // @ts-ignore this.setMaterial(engine._basicResources.textDefaultMaterial);If accessing private or protected members, consider exposing them through public getters or adjusting the class design.
Committable suggestion skipped: line range outside the PR's diff.
packages/ui/src/component/UICanvas.ts (6)
587-588:
⚠️ Potential issueAdd a break statement to prevent unintended fall-through
In the
switch
statement at line 586, thecase CanvasRenderMode.ScreenSpaceOverlay
lacks abreak
, causing it to fall through to the next case unintentionally.Apply this diff to add the missing
break
:case CanvasRenderMode.ScreenSpaceOverlay: this._removeCanvasListener(); + break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case CanvasRenderMode.ScreenSpaceOverlay: this._removeCanvasListener(); break;
🧰 Tools
🪛 Biome
[error] 587-588: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
159-159: 🛠️ Refactor suggestion
Avoid assignment within a logical expression for better readability
On line 159, assigning
this.scene._componentsManager._overlayCanvasesSortingFlag = true
within a logical expression can be confusing and reduce code clarity.Apply this diff to improve readability:
this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay && // @ts-ignore - (this.scene._componentsManager._overlayCanvasesSortingFlag = true); + this.scene._componentsManager._overlayCanvasesSortingFlag = true;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.this.scene._componentsManager._overlayCanvasesSortingFlag = true;
🧰 Tools
🪛 Biome
[error] 159-159: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
597-598:
⚠️ Potential issueAdd a break statement to prevent unintended fall-through
At line 597, the
case CanvasRenderMode.ScreenSpaceOverlay
is missing abreak
statement, leading to unintended execution of subsequent cases.Apply this diff to correct the switch case:
case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); + break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case CanvasRenderMode.ScreenSpaceOverlay: this._addCanvasListener(); break;
🧰 Tools
🪛 Biome
[error] 597-598: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
599-601:
⚠️ Potential issueAdd a break statement to prevent unintended fall-through
The
case CanvasRenderMode.ScreenSpaceCamera
at line 599 lacks abreak
, causing unintended fall-through to the next case.Apply this diff to add the missing
break
:case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); + break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case CanvasRenderMode.ScreenSpaceCamera: this._adapterPoseInScreenSpace(); this._adapterSizeInScreenSpace(); break;
🧰 Tools
🪛 Biome
[error] 599-601: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
406-408:
⚠️ Potential issueAdd a break statement to prevent unintended fall-through
The
case ComponentType.UIRenderer
lacks abreak
statement, causing it to fall through toComponentType.UIInteractive
, which may not be the intended behavior.Apply this diff to add the missing
break
:case ComponentType.UIRenderer: elements[depth] = component as unknown as IGraphics; ++depth; + break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.case ComponentType.UIRenderer: elements[depth] = component as unknown as IGraphics; ++depth; break;
🧰 Tools
🪛 Biome
[error] 406-408: This case is falling through to the next case.
Add a
break
orreturn
statement to the end of this case to prevent fallthrough.(lint/suspicious/noFallthroughSwitchClause)
237-237:
⚠️ Potential issueWrap variable declarations in switch cases with blocks to prevent scope issues
Declaring variables directly within a
switch
case can cause them to be accessible in other cases due to JavaScript's function-level scoping.Apply this diff to encapsulate the declaration:
case CanvasRenderMode.ScreenSpaceOverlay: + { const { min, max } = renderer.bounds; if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { continue; } break; + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 237-237: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/design/src/ui/IUIElement.ts (1)
1-4: 🛠️ Refactor suggestion
Consider revising member naming conventions and visibility.
The interface members use underscore prefix suggesting they are private/internal, which is unconventional for interface contracts. Interface members typically define the public API contract.
Consider this alternative approach:
export interface IUIElement { - _onUIUpdateIndex: number; - _onUpdate(): void; + updateIndex: number; + update(): void; }Additionally, please add JSDoc comments to document:
- The purpose of this interface
- The role of
updateIndex
in controlling update order/priority- When and how the
update
method is calledCommittable suggestion skipped: line range outside the PR's diff.
packages/design/src/ui/IUICanvas.ts (2)
2-2: 🛠️ Refactor suggestion
⚠️ Potential issueReplace
any
types with specific types for better type safety.Using
any
type bypasses TypeScript's type checking and can lead to runtime errors. Consider defining proper types for these members.Consider defining specific types:
import { Entity } from '../core/Entity'; import { RenderContext } from '../renderer/RenderContext'; import { RenderElement } from '../renderer/RenderElement'; export interface UICanvas { entity: Entity; // ... other members _renderElement: RenderElement; _prepareRender(renderContext: RenderContext): void; }Also applies to: 5-5, 6-6
4-5: 🛠️ Refactor suggestion
Remove private members from the interface definition.
The interface includes members prefixed with underscore, suggesting they are private implementation details. Interfaces should define the public contract, not implementation details.
Consider moving private members to the implementing class:
export interface UICanvas { entity: Entity; sortOrder: number; prepareRender(renderContext: RenderContext): void; } // In the implementing class: class Canvas implements UICanvas { private _canvasIndex: number; private _renderElement: RenderElement; // ... implementation }packages/core/src/2d/text/ITextRenderer.ts (1)
4-8: 🛠️ Refactor suggestion
Consider removing underscore prefix from public interface method
The method
_getSubFont()
uses an underscore prefix, which typically indicates a private or internal member. However, this is part of a public interface and TypeScript has proper visibility modifiers that should be used instead.Consider this alternative:
export interface ITextRenderer { text: string; overflowMode: OverflowMode; - _getSubFont(): SubFont; + getSubFont(): SubFont; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.export interface ITextRenderer { text: string; overflowMode: OverflowMode; getSubFont(): SubFont; }
packages/ui/src/interface/IGraphics.ts (1)
8-8: 🛠️ Refactor suggestion
Consider a more TypeScript-idiomatic approach for the raycast method.
Several concerns with the current method signature:
- The underscore prefix suggests an internal implementation, but TypeScript has built-in visibility modifiers that could be more appropriate.
- Using an out parameter (
HitResult
) is not a typical TypeScript pattern. Consider returning aHitResult | null
instead.- The purpose of the
distance
parameter isn't clear from the signature.Consider this alternative approach:
- _raycast(ray: Ray, out: HitResult, distance: number): boolean; + /** Performs raycasting against this graphics element + * @param ray The ray to test against + * @param maxDistance The maximum distance to test for intersections + * @returns The hit result if an intersection occurred, null otherwise + */ + protected raycast(ray: Ray, maxDistance: number): HitResult | null;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** Performs raycasting against this graphics element * @param ray The ray to test against * @param maxDistance The maximum distance to test for intersections * @returns The hit result if an intersection occurred, null otherwise */ protected raycast(ray: Ray, maxDistance: number): HitResult | null;
packages/ui/src/interface/IElement.ts (2)
8-10: 🛠️ Refactor suggestion
Consider encapsulating internal state.
Internal state properties prefixed with
_
should not be exposed in the public interface. Consider:
- Moving these to a separate internal interface
- Using private fields in the implementing class
// Example approach using a separate internal interface interface IElementInternal { _indexInCanvas: number; _isCanvasDirty: boolean; } export interface IElement { entity: Entity; readonly canvas: UICanvas; }
11-12: 🛠️ Refactor suggestion
Improve type safety and encapsulation of event handling.
The current event handling implementation has several issues:
- The
flag
parameter uses a number type instead of an enum- The
param
parameter is loosely typed asany
- The listener array is exposed as an internal property
Consider this improved approach:
/** Event types for canvas interactions */ export enum CanvasEventType { // Define your event types here } /** Event data structure */ export interface CanvasEventData { // Define strongly typed event parameters } export interface IElement { // ... other members ... /** Add a canvas event listener */ addEventListener(entity: Entity, callback: (event: CanvasEventType, data?: CanvasEventData) => void): void; /** Remove a canvas event listener */ removeEventListener(entity: Entity): void; }packages/core/src/enums/ComponentType.ts (1)
22-22:
⚠️ Potential issueFix incorrect bitwise combination value
The value
0x680
forUIElement
appears to include an extra bit that doesn't correspond to any of its component flags. If it's meant to be a combination of UICanvas (0x80), UIGroup (0x100), and UIRenderer (0x200), the value should be0x380
.- UIElement = 0x680 + UIElement = 0x380 // UICanvas | UIGroup | UIRenderer📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.UIElement = 0x380 // UICanvas | UIGroup | UIRenderer
packages/ui/src/interface/IGroupAble.ts (2)
14-16: 🛠️ Refactor suggestion
Review method visibility and callback type safety
A few concerns about the method declarations:
_onUpdate
and_onGroupModify
are internal methods but part of the public interface_groupListener
uses a genericnumber
type for the flag parameter, while_onGroupModify
uses the more specificGroupModifyFlags
Consider these improvements:
export interface IGroupAble extends IElement { readonly group: UIGroup; } interface IGroupableInternal extends IGroupAble { _onUpdate?(): void; _onGroupModify(flag: GroupModifyFlags): void; _groupListener: (flag: GroupModifyFlags) => void; // Use specific enum type }
1-17: 💡 Codebase verification
Based on the search results, I can now generate the final response:
Optional properties in IGroupAble are implemented differently across classes
The verification shows that these properties are not truly optional in implementations:
_onUIUpdateIndex
is initialized to 0 in both UIInteractive and UIRenderer_globalInteractive
is initialized totrue
in UIGroup andfalse
in UIInteractive_globalAlpha
is initialized to 1 in UIGroup_onUpdate
is implemented in UIInteractive but not in UIGroupThe interface should reflect the actual implementation requirements by removing the optional markers (
?
) since these properties are always present in implementing classes.🔗 Analysis chain
Verify usage of optional properties
The interface has several optional properties (
_onUIUpdateIndex
,_globalAlpha
,_globalInteractive
,_onUpdate
). We should verify if these are truly optional or if they're always set during initialization.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for implementations to verify optional property usage ast-grep --pattern 'class $_ implements IGroupAble { $$$ }' # Search for property assignments rg -A 5 "_onUIUpdateIndex|_globalAlpha|_globalInteractive" --type typescriptLength of output: 177
Script:
#!/bin/bash # Search for implementations using different file extensions ast-grep --pattern 'class $_ implements IGroupAble { $$$ }' # Search for property assignments with .ts and .tsx extensions rg -t ts -t tsx -A 5 "_onUIUpdateIndex|_globalAlpha|_globalInteractive" # Search for property initialization patterns rg -t ts -t tsx "constructor.*IGroupAble" -A 20 # Search for files that might implement IGroupAble fd -e ts -e tsx | xargs grep -l "implements IGroupAble"Length of output: 393
Script:
#!/bin/bash # Let's examine the content of the implementing files cat packages/ui/src/component/UIGroup.ts packages/ui/src/component/interactive/UIInteractive.ts # Search for property assignments without file type flags rg "_onUIUpdateIndex|_globalAlpha|_globalInteractive" # Search for constructor initialization rg "constructor.*{" -A 10 packages/ui/src/component/UIGroup.ts packages/ui/src/component/interactive/UIInteractive.tsLength of output: 15476
packages/ui/src/component/interactive/transition/ScaleTransition.ts (1)
22-24:
⚠️ Potential issueAdd null check for target entity.
The
_applyValue
method should include a null check before accessing the target entity.protected override _applyValue(value: number): void { + if (!this._target?.entity) { + return; + } this._target.entity.transform.setScale(value, value, value); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected override _applyValue(value: number): void { if (!this._target?.entity) { return; } this._target.entity.transform.setScale(value, value, value); }
packages/core/src/2d/index.ts (1)
3-5: 💡 Codebase verification
Documentation needed for sprite assembler system
The sprite documentation (
sprite.md
) lacks information about the different sprite assembler types and their use cases. While it covers sprite properties and general usage, it doesn't explain:
- The purpose of Simple, Sliced, and Tiled sprite assemblers
- When to use each assembler type
- How these assemblers interact with the sprite rendering system
The implementation files also lack comprehensive documentation comments explaining their architectural roles.
Suggested documentation points:
- Add a new section in
sprite.md
about sprite assembly modes- Include code-level documentation in assembler classes
- Consider creating a dedicated guide for sprite rendering architecture
🔗 Analysis chain
Consider adding architectural documentation for the new assembler system.
The introduction of three distinct sprite assemblers (Simple, Sliced, Tiled) suggests a significant architectural enhancement. This warrants documentation explaining:
- The purpose and use case for each assembler type
- How to choose between different assemblers
- The relationship between assemblers and renderers
Let's check if documentation exists:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Look for documentation about the assembler system # Search for documentation files echo "Searching for documentation files:" fd -e md -e txt . | grep -i "assembler\|sprite\|gui" # Search for documentation comments in assembler files echo "Searching for documentation comments in assembler files:" rg "\/\*\*|\*\/" "./assembler/"Length of output: 708
Script:
#!/bin/bash # Let's check the content of relevant documentation and assembler files # Check English documentation content echo "=== Sprite Documentation Content ===" cat docs/en/graphics/2D/sprite.md # Check assembler files for documentation echo -e "\n=== Assembler Implementation Documentation ===" rg -A 5 "class.*Assembler" "packages/core/src/2d/assembler/"Length of output: 6836
packages/ui/src/component/interactive/transition/SpriteTransition.ts (1)
34-44:
⚠️ Potential issueAdd null checks and error handling for robustness.
The value management methods need additional safety checks:
_getTargetValueCopy
might return undefined when_target
is null_updateCurrentValue
lacks validation for null/undefined values_applyValue
might throw ifthis._target
is nullConsider adding these safety checks:
- protected _getTargetValueCopy(): Sprite { - return this._target?.sprite; + protected _getTargetValueCopy(): Sprite | null { + return this._target?.sprite ?? null; } protected override _updateCurrentValue(srcValue: Sprite, destValue: Sprite, weight: number): void { + if (!srcValue && !destValue) { + this._currentValue = null; + return; + } this._currentValue = weight >= 1 ? destValue : srcValue; } protected override _applyValue(value: Sprite): void { + if (!this._target) { + return; + } this._target.sprite = value || this._normal; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected _getTargetValueCopy(): Sprite | null { return this._target?.sprite ?? null; } protected override _updateCurrentValue(srcValue: Sprite, destValue: Sprite, weight: number): void { if (!srcValue && !destValue) { this._currentValue = null; return; } this._currentValue = weight >= 1 ? destValue : srcValue; } protected override _applyValue(value: Sprite): void { if (!this._target) { return; } this._target.sprite = value || this._normal; }
packages/ui/src/component/advanced/Button.ts (2)
32-35: 🛠️ Refactor suggestion
Improve cleanup implementation and readability.
- The current cleanup only marks listeners as destroyed but doesn't clear the array.
- The assignment in expression makes the code less maintainable.
Consider this improved implementation:
override onDestroy(): void { super.onDestroy(); - this._listeners.findAndRemove((value) => (value.destroyed = true)); + this._listeners.forEach(listener => listener.destroyed = true); + this._listeners.clear(); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.override onDestroy(): void { super.onDestroy(); this._listeners.forEach(listener => listener.destroyed = true); this._listeners.clear(); }
🧰 Tools
🪛 Biome
[error] 34-34: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
11-21: 🛠️ Refactor suggestion
Improve code readability and documentation.
- The assignment within the expression makes the code harder to read and maintain.
- The method documentation could be more descriptive about the event parameter.
Consider these improvements:
/** * Add a listening function for click. * @param listener - The listening function + * @param event - The pointer event data containing click information */ addClicked(listener: (event: PointerEventData) => void): void { this._listeners.push({ fn: listener }); } /** * Remove a listening function of click. * @param listener - The listening function + * @returns boolean - True if the listener was found and removed */ removeClicked(listener: (event: PointerEventData) => void): void { - this._listeners.findAndRemove((value) => (value.fn === listener ? (value.destroyed = true) : false)); + this._listeners.findAndRemove((value) => { + if (value.fn === listener) { + value.destroyed = true; + return true; + } + return false; + }); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements./** * Add a listening function for click. * @param listener - The listening function * @param event - The pointer event data containing click information */ addClicked(listener: (event: PointerEventData) => void): void { this._listeners.push({ fn: listener }); } /** * Remove a listening function of click. * @param listener - The listening function * @returns boolean - True if the listener was found and removed */ removeClicked(listener: (event: PointerEventData) => void): void { this._listeners.findAndRemove((value) => { if (value.fn === listener) { value.destroyed = true; return true; } return false; }); }
🧰 Tools
🪛 Biome
[error] 20-20: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/ui/UIUtils.ts (2)
10-14: 🛠️ Refactor suggestion
Consider refactoring to a module pattern with initialized fields
The current implementation as a static-only class could be improved by converting it to a module with standalone functions and properly initialized fields. This would align with TypeScript best practices and prevent potential race conditions.
-export class UIUtils { - private static _renderQueue: RenderQueue; - private static _virtualCamera: VirtualCamera; - private static _viewport: Vector4; +const _renderQueue = new RenderQueue(RenderQueueType.Transparent); +const _virtualCamera = new VirtualCamera(); +const _viewport = new Vector4(0, 0, 1, 1); + +export function render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { // ... implementation -}Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 10-39: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
15-19: 🛠️ Refactor suggestion
Improve initialization pattern and add input validation
The current initialization pattern uses assignments within expressions and lacks input validation. This can be improved for better readability and robustness.
static render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { + if (!engine || !uiCanvases) { + throw new Error("Engine and uiCanvases parameters are required"); + } + - const uiRenderQueue = (this._renderQueue ||= new RenderQueue(RenderQueueType.Transparent)); - const virtualCamera = (this._virtualCamera ||= new VirtualCamera()); - const viewport = (this._viewport ||= new Vector4(0, 0, 1, 1)); + const uiRenderQueue = UIUtils._renderQueue; + const virtualCamera = UIUtils._virtualCamera; + const viewport = UIUtils._viewport;Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 16-16: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 16-16: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 17-17: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 17-17: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
[error] 18-18: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 18-18: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.(lint/complexity/noThisInStatic)
packages/ui/src/component/interactive/transition/ColorTransition.ts (2)
22-29:
⚠️ Potential issueAddress type safety concerns with
@ts-ignore
.Multiple
@ts-ignore
comments suggest potential type safety issues. Consider:
- Properly typing the Color class to include
_onValueChanged
- Using a proper event emitter pattern instead of direct property assignment
// Example type definition to consider interface TransitionColor extends Color { _onValueChanged?: () => void; }
48-64:
⚠️ Potential issueAdd null safety checks in protected methods.
Several methods could benefit from additional null safety checks:
_getTargetValueCopy
: The optional chaining on_target?.color
is good, but reusingthis._color
for multiple operations could lead to side effects_applyValue
: Missing null check forthis._target
protected _getTargetValueCopy(): Color { - const color = this._color; + const color = new Color(); color.copyFrom(this._target?.color || this._normal); return color; } protected override _applyValue(value: Color): void { + if (!this._target) { + return; + } this._target.color = value; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.protected _getTargetValueCopy(): Color { const color = new Color(); color.copyFrom(this._target?.color || this._normal); return color; } protected override _updateCurrentValue(srcValue: Color, destValue: Color, weight: number): void { if (weight >= 1) { this._currentValue.copyFrom(destValue); } else { Color.lerp(srcValue, destValue, weight, this._currentValue); } } protected override _applyValue(value: Color): void { if (!this._target) { return; } this._target.color = value; }
packages/core/src/ComponentsDependencies.ts (2)
97-101: 🛠️ Refactor suggestion
Add JSDoc documentation and type validation
The decorator lacks documentation and type validation. Consider:
- Adding JSDoc documentation to explain its purpose and usage
- Adding validation to ensure the target is a valid component constructor
+/** + * Decorator that marks a component to be automatically inherited by child entities. + * Used primarily for GUI components that should propagate through the entity hierarchy. + * @example + * ```ts + * @markAsInherited() + * class UIComponent extends Component { + * // ... + * } + * ``` + */ export function markAsInherited() { return function <T extends ComponentConstructor>(target: T): void { + if (!(target.prototype instanceof Component)) { + throw new Error(`${target.name} must be a Component class`); + } ComponentsDependencies._inheritedMap.set(target, true); }; }
14-14: 🛠️ Refactor suggestion
Use lowercase
boolean
type instead ofBoolean
For consistency with TypeScript best practices, use the primitive type
boolean
instead of the object wrapper typeBoolean
.- static _inheritedMap = new Map<ComponentConstructor, Boolean>(); + static _inheritedMap = new Map<ComponentConstructor, boolean>();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.static _inheritedMap = new Map<ComponentConstructor, boolean>();
🧰 Tools
🪛 Biome
[error] 14-14: Don't use 'Boolean' as a type.
Use lowercase primitives for consistency.
Safe fix: Use 'boolean' instead(lint/complexity/noBannedTypes)
packages/core/src/2d/assembler/SlicedSpriteAssembler.ts (2)
65-69: 🛠️ Refactor suggestion
Improve readability by separating combined assignments
The combined assignment statements make the code harder to read and maintain. Consider breaking them down into separate lines.
Apply this diff to improve readability:
- (row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); - row[3] = width - expectWidth * (1 - right) * widthScale; + row[0] = expectWidth * left * widthScale; + row[1] = fixedLeft * widthScale; + row[2] = fixedLeft * widthScale; + row[3] = width - expectWidth * (1 - right) * widthScale; - (row[0] = expectWidth * left), (row[1] = fixedLeft), (row[2] = width - fixedRight); - row[3] = width - expectWidth * (1 - right); + row[0] = expectWidth * left; + row[1] = fixedLeft; + row[2] = width - fixedRight; + row[3] = width - expectWidth * (1 - right); - (column[0] = expectHeight * bottom * heightScale), (column[1] = column[2] = fixedBottom * heightScale); - column[3] = height - expectHeight * (1 - top) * heightScale; + column[0] = expectHeight * bottom * heightScale; + column[1] = fixedBottom * heightScale; + column[2] = fixedBottom * heightScale; + column[3] = height - expectHeight * (1 - top) * heightScale; - (column[0] = expectHeight * bottom), (column[1] = fixedBottom), (column[2] = height - fixedTop); - column[3] = height - expectHeight * (1 - top); + column[0] = expectHeight * bottom; + column[1] = fixedBottom; + column[2] = height - fixedTop; + column[3] = height - expectHeight * (1 - top);Also applies to: 74-78
169-173: 🛠️ Refactor suggestion
Improve readability of UV calculations
Similar to earlier feedback, the combined assignment statements here should be separated for better readability.
Apply this diff:
- (row[0] = expectWidth * left * widthScale), (row[1] = row[2] = fixedLeft * widthScale); - row[3] = width - expectWidth * (1 - right) * widthScale; + row[0] = expectWidth * left * widthScale; + row[1] = fixedLeft * widthScale; + row[2] = fixedLeft * widthScale; + row[3] = width - expectWidth * (1 - right) * widthScale; - (row[0] = expectWidth * left), (row[1] = fixedLeft), (row[2] = width - fixedRight); - row[3] = width - expectWidth * (1 - right); + row[0] = expectWidth * left; + row[1] = fixedLeft; + row[2] = width - fixedRight; + row[3] = width - expectWidth * (1 - right); - (column[0] = expectHeight * bottom * heightScale), (column[1] = column[2] = fixedBottom * heightScale); - column[3] = height - expectHeight * (1 - top) * heightScale; + column[0] = expectHeight * bottom * heightScale; + column[1] = fixedBottom * heightScale; + column[2] = fixedBottom * heightScale; + column[3] = height - expectHeight * (1 - top) * heightScale; - (column[0] = expectHeight * bottom), (column[1] = fixedBottom), (column[2] = height - fixedTop); - column[3] = height - expectHeight * (1 - top); + column[0] = expectHeight * bottom; + column[1] = fixedBottom; + column[2] = height - fixedTop; + column[3] = height - expectHeight * (1 - top);Also applies to: 178-182
packages/core/src/ComponentsManager.ts (2)
272-276: 🛠️ Refactor suggestion
Align callUIOnUpdate with other update methods
The implementation is inconsistent with other update methods in the class. Other methods maintain indices during iteration.
Apply this change to maintain consistency:
callUIOnUpdate(deltaTime: number): void { - this._onUpdateUIElements.forEach((element: IUIElement) => { - element._onUpdate(); - }); + this._onUpdateUIElements.forEach( + (element: IUIElement) => { + element._onUpdate(); + }, + (element: IUIElement, index: number) => { + element._onUIUpdateIndex = index; + } + ); }Committable suggestion skipped: line range outside the PR's diff.
185-188:
⚠️ Potential issueFix incorrect array reference in addOnUpdateUIElement
There's a critical bug in the implementation. The method is using
this._onUpdateRenderers.length
instead ofthis._onUpdateUIElements.length
for the index calculation.Apply this fix:
addOnUpdateUIElement(element: IUIElement): void { - element._onUIUpdateIndex = this._onUpdateRenderers.length; + element._onUIUpdateIndex = this._onUpdateUIElements.length; this._onUpdateUIElements.add(element); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.addOnUpdateUIElement(element: IUIElement): void { element._onUIUpdateIndex = this._onUpdateUIElements.length; this._onUpdateUIElements.add(element); }
packages/core/src/2d/sprite/SpriteRenderer.ts (1)
421-451: 💡 Codebase verification
Based on the provided code snippet and the enum values found (
texture
,size
,atlasRotate
,atlasRegion
,atlasRegionOffset
,border
), I can now generate the final response.Missing handlers for several sprite modification flags
The current switch statement only handles
size
andborder
modifications, but theSpriteModifyFlags
enum defines additional flags that should be handled:
texture
(0x1)atlasRotate
(0x4)atlasRegion
(0x8)atlasRegionOffset
(0x10)🔗 Analysis chain
Verify the completeness of sprite modification handling
While the switch statement handles various modification types, we should verify if all possible sprite modifications are covered.
Let me check the complete sprite modification handling by looking at the switch statement in the file.
Let me try a different approach to find the sprite modification handling code.
Let me try one more search with a broader pattern to find the sprite modification handling.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for all possible sprite modification flags rg -A 5 "enum SpriteModifyFlags"Length of output: 455
Script:
#!/bin/bash # Search for the switch statement handling sprite modifications rg -B 5 -A 30 "switch.*modifyFlags" packages/core/src/2d/sprite/SpriteRenderer.tsLength of output: 83
Script:
#!/bin/bash # Search for the sprite modification handling in SpriteRenderer rg -B 5 -A 30 "_onSprite.*Modified" packages/core/src/2d/sprite/SpriteRenderer.tsLength of output: 83
Script:
#!/bin/bash # Search for any method handling sprite modifications in SpriteRenderer rg -B 5 -A 30 "modifyFlags" packages/core/src/2d/sprite/SpriteRenderer.tsLength of output: 73
packages/core/src/2d/assembler/TiledSpriteAssembler.ts (2)
81-86:
⚠️ Potential issueReplace global isNaN with Number.isNaN
The global isNaN function performs type coercion which can lead to unexpected results. Use Number.isNaN instead for more precise NaN checking.
- if (isNaN(cUV.get(doubleJ)) || isNaN(cUV.get(doubleJ + 1))) { + if (Number.isNaN(cUV.get(doubleJ)) || Number.isNaN(cUV.get(doubleJ + 1))) { - if (isNaN(rUV.get(doubleI)) || isNaN(rUV.get(doubleI + 1))) { + if (Number.isNaN(rUV.get(doubleI)) || Number.isNaN(rUV.get(doubleI + 1))) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (Number.isNaN(cUV.get(doubleJ)) || Number.isNaN(cUV.get(doubleJ + 1))) { continue; } for (let i = 0; i < rowLength; i++) { const doubleI = i << 1; if (Number.isNaN(rUV.get(doubleI)) || Number.isNaN(rUV.get(doubleI + 1))) {
🧰 Tools
🪛 Biome
[error] 81-81: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 81-81: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 86-86: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
[error] 86-86: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
422-444:
⚠️ Potential issueAdd block scoping to switch cases
Variables declared in switch cases can leak into other cases. Wrap case blocks in curly braces to prevent variable scope leakage.
switch (rType) { case TiledType.Compressed: { const scale = width / fixedLR; rPos.add(expectWidth * left * scale), rPos.add(fixedL * scale); rPos.add(width - expectWidth * (1 - right) * scale); rUV.add(spriteUV0.x), rUV.add(spriteUV1.x), rUV.add(spriteUV2.x), rUV.add(spriteUV3.x); break; + } case TiledType.WithoutTiled: { // ... existing code ... break; + } case TiledType.WithTiled: { // ... existing code ... break; + }Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome
[error] 422-422: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 434-434: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 435-435: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 436-436: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
packages/core/src/Entity.ts (1)
613-628: 🛠️ Refactor suggestion
Refactor assignment in expression for better readability.
The modification listener system is well-designed, but the assignment in expression should be refactored for clarity.
Apply this diff to improve readability:
_registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { - (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); + if (!this._updateFlagManager) { + this._updateFlagManager = new UpdateFlagManager(); + } + this._updateFlagManager.addListener(onChange); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements._registerModifyListener(onChange: (flag: EntityModifyFlags) => void): void { if (!this._updateFlagManager) { this._updateFlagManager = new UpdateFlagManager(); } this._updateFlagManager.addListener(onChange); } /** * @internal */ _unRegisterModifyListener(onChange: (flag: EntityModifyFlags) => void): void { this._updateFlagManager?.removeListener(onChange); } /** * @internal */ _dispatchModify(flag: EntityModifyFlags, param?: any): void { this._updateFlagManager?.dispatch(flag, param);
🧰 Tools
🪛 Biome
[error] 614-614: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/Camera.ts (1)
738-747: 🛠️ Refactor suggestion
Refactor the assignment in expression
The assignment within the expression can make the code harder to understand and maintain.
Consider refactoring to improve readability:
- (this._updateFlagManager ||= new UpdateFlagManager()).addListener(onChange); + if (!this._updateFlagManager) { + this._updateFlagManager = new UpdateFlagManager(); + } + this._updateFlagManager.addListener(onChange);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements._registerModifyListener(onChange: (flag: CameraModifyFlags) => void): void { if (!this._updateFlagManager) { this._updateFlagManager = new UpdateFlagManager(); } this._updateFlagManager.addListener(onChange); } /** * @internal */ _unRegisterModifyListener(onChange: (flag: CameraModifyFlags) => void): void { this._updateFlagManager?.removeListener(onChange); }
🧰 Tools
🪛 Biome
[error] 739-739: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
fix lint error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (7)
packages/ui/package.json (1)
26-30
: Review completeness of included filesThe
files
array should include all necessary files while excluding development files.Consider adding the following to ensure proper packaging:
- Add
README.md
to the files array for npm registry documentation- Add
LICENSE
file reference- Consider adding
package.json
explicitly"files": [ "dist/**/*", "libs/**/*", "types/**/*", + "README.md", + "LICENSE", + "package.json" ],packages/core/src/animation/Keyframe.ts (2)
13-22
: Fix indentation according to project style guidelines.The conditional type expressions need consistent indentation to match the project's formatting rules.
Apply this formatting fix:
? Vector2 - : V extends Vector3 - ? Vector3 - : V extends Vector4 | Color | Quaternion | Rect - ? Vector4 - : V extends number[] | Float32Array - ? number[] - : V extends ReferResource - ? ReferResource - : never + : V extends Vector3 + ? Vector3 + : V extends Vector4 | Color | Quaternion | Rect + ? Vector4 + : V extends number[] | Float32Array + ? number[] + : V extends ReferResource + ? ReferResource + : never🧰 Tools
🪛 eslint
[error] 13-13: Insert
··
(prettier/prettier)
[error] 14-14: Insert
··
(prettier/prettier)
[error] 15-15: Insert
····
(prettier/prettier)
[error] 16-16: Insert
····
(prettier/prettier)
[error] 17-17: Replace
····
with··········
(prettier/prettier)
[error] 18-18: Insert
······
(prettier/prettier)
[error] 19-19: Insert
········
(prettier/prettier)
[error] 20-20: Insert
········
(prettier/prettier)
[error] 21-21: Insert
··········
(prettier/prettier)
[error] 22-22: Insert
··········
(prettier/prettier)
🪛 GitHub Check: lint
[failure] 13-13:
Insert··
[failure] 14-14:
Insert··
[failure] 15-15:
Insert····
[failure] 16-16:
Insert····
[failure] 17-17:
Replace····
with··········
[failure] 18-18:
Insert······
[failure] 19-19:
Insert········
[failure] 20-20:
Insert········
[failure] 21-21:
Insert··········
[failure] 22-22:
Insert··········
Line range hint
1-22
: LGTM! Well-structured type definition.The generic type parameter
T
effectively maps input types to their appropriate value types, maintaining type safety throughout the animation system. The conditional type expressions handle all possibleKeyframeValueType
variants correctly.Consider documenting the type mapping logic in the class JSDoc to help other developers understand the relationship between
V
andT
type parameters.🧰 Tools
🪛 eslint
[error] 13-13: Insert
··
(prettier/prettier)
[error] 14-14: Insert
··
(prettier/prettier)
[error] 15-15: Insert
····
(prettier/prettier)
[error] 16-16: Insert
····
(prettier/prettier)
[error] 17-17: Replace
····
with··········
(prettier/prettier)
[error] 18-18: Insert
······
(prettier/prettier)
[error] 19-19: Insert
········
(prettier/prettier)
[error] 20-20: Insert
········
(prettier/prettier)
[error] 21-21: Insert
··········
(prettier/prettier)
[error] 22-22: Insert
··········
(prettier/prettier)
🪛 GitHub Check: lint
[failure] 13-13:
Insert··
[failure] 14-14:
Insert··
[failure] 15-15:
Insert····
[failure] 16-16:
Insert····
[failure] 17-17:
Replace····
with··········
[failure] 18-18:
Insert······
[failure] 19-19:
Insert········
[failure] 20-20:
Insert········
[failure] 21-21:
Insert··········
[failure] 22-22:
Insert··········
packages/core/src/animation/internal/animationCurveOwner/AnimationCurveOwner.ts (2)
95-96
: Consider refactoring nested ternary expressions for better readability.While the logic is correct, the nested ternary expressions make the code harder to maintain. Consider extracting this logic into a separate method or using if-else statements.
Fix the indentation and refactor for better readability:
- ? this.cureType._getZeroValue(this.baseEvaluateData.value) - : this.defaultValue; + ? this.cureType._getZeroValue(this.baseEvaluateData.value) + : this.defaultValue;Alternative implementation using a method:
private _getFallbackValue(evaluateData: IEvaluateData<V>, additive: boolean): V { return additive ? this.cureType._getZeroValue(evaluateData.value) : this.defaultValue; }🧰 Tools
🪛 eslint
[error] 95-95: Insert
··
(prettier/prettier)
[error] 96-96: Insert
··
(prettier/prettier)
104-105
: Apply consistent formatting and refactoring.For consistency with the previous changes, apply the same formatting and consider using the suggested
_getFallbackValue
method here as well.- ? this.cureType._getZeroValue(this.crossEvaluateData.value) - : this.defaultValue; + ? this.cureType._getZeroValue(this.crossEvaluateData.value) + : this.defaultValue;🧰 Tools
🪛 eslint
[error] 104-104: Insert
··
(prettier/prettier)
[error] 105-105: Insert
··
(prettier/prettier)
packages/core/src/RenderPipeline/BasicRenderPipeline.ts (1)
346-362
: Consider caching bounds check resultsThe bounds intersection check against the frustum is performed for every renderer on each frame. For static objects that don't move, this check could be cached until the object or camera moves.
Example optimization:
if (enableFrustumCulling) { - if (!frustum.intersectsBox(renderer.bounds)) { + if (!renderer._isMoving && renderer._cachedFrustumResult !== undefined) { + if (!renderer._cachedFrustumResult) continue; + } else { + renderer._cachedFrustumResult = frustum.intersectsBox(renderer.bounds); + if (!renderer._cachedFrustumResult) continue; + } continue; }packages/core/src/Engine.ts (1)
518-522
: Consider performance optimization for UI renderingWhile the UI rendering implementation is correct, consider these performance optimizations:
- Cache the length check result of
uiCanvas
to avoid multiple array access- Consider adding a dirty flag system to skip UI sorting and rendering when no changes occurred
Here's a suggested optimization:
- const uiCanvas = componentsManager._overlayCanvases; - if (uiCanvas.length > 0) { - componentsManager.sortUICanvases(); - UIUtils.render(this, uiCanvas); - } + const uiCanvases = componentsManager._overlayCanvases; + const uiCanvasCount = uiCanvases.length; + if (uiCanvasCount > 0) { + if (componentsManager._isUIDirty) { + componentsManager.sortUICanvases(); + componentsManager._isUIDirty = false; + } + UIUtils.render(this, uiCanvases); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
packages/core/src/BasicResources.ts
(3 hunks)packages/core/src/Engine.ts
(4 hunks)packages/core/src/RenderPipeline/BasicRenderPipeline.ts
(2 hunks)packages/core/src/animation/Keyframe.ts
(1 hunks)packages/core/src/animation/internal/animationCurveOwner/AnimationCurveOwner.ts
(2 hunks)packages/core/src/particle/ParticleRenderer.ts
(3 hunks)packages/ui/package.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/core/src/BasicResources.ts
- packages/core/src/particle/ParticleRenderer.ts
🧰 Additional context used
🪛 eslint
packages/core/src/animation/Keyframe.ts
[error] 13-13: Insert ··
(prettier/prettier)
[error] 14-14: Insert ··
(prettier/prettier)
[error] 15-15: Insert ····
(prettier/prettier)
[error] 16-16: Insert ····
(prettier/prettier)
[error] 17-17: Replace ····
with ··········
(prettier/prettier)
[error] 18-18: Insert ······
(prettier/prettier)
[error] 19-19: Insert ········
(prettier/prettier)
[error] 20-20: Insert ········
(prettier/prettier)
[error] 21-21: Insert ··········
(prettier/prettier)
[error] 22-22: Insert ··········
(prettier/prettier)
packages/core/src/animation/internal/animationCurveOwner/AnimationCurveOwner.ts
[error] 95-95: Insert ··
(prettier/prettier)
[error] 96-96: Insert ··
(prettier/prettier)
[error] 104-104: Insert ··
(prettier/prettier)
[error] 105-105: Insert ··
(prettier/prettier)
[error] 129-129: Insert ··
(prettier/prettier)
[error] 130-130: Insert ··
(prettier/prettier)
🪛 GitHub Check: lint
packages/core/src/animation/Keyframe.ts
[failure] 13-13:
Insert ··
[failure] 14-14:
Insert ··
[failure] 15-15:
Insert ····
[failure] 16-16:
Insert ····
[failure] 17-17:
Replace ····
with ··········
[failure] 18-18:
Insert ······
[failure] 19-19:
Insert ········
[failure] 20-20:
Insert ········
[failure] 21-21:
Insert ··········
[failure] 22-22:
Insert ··········
🪛 Biome
packages/core/src/animation/internal/animationCurveOwner/AnimationCurveOwner.ts
[error] 99-99: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🔇 Additional comments (9)
packages/ui/package.json (3)
2-3
: Verify version alignment with core package
The package version is set to 1.3.20. Since this is a new UI package that depends on @galacean/engine, we should verify version alignment.
✅ Verification successful
Version alignment verified across all Galacean packages
All Galacean packages, including the core engine and UI package, are consistently versioned at 1.3.20. This alignment is correct and follows proper monorepo versioning practices.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check version alignment between UI package and core engine
echo "Checking core engine version..."
jq -r '.version' packages/core/package.json || echo "Core package.json not found"
# List all related packages and their versions for consistency
echo -e "\nChecking all related package versions..."
fd -t f package.json -x jq -r '. | select(.name | startswith("@galacean")) | .name + ": " + .version'
Length of output: 899
31-36
: Verify workspace dependency configuration
The package uses workspace dependencies for @galacean/engine in both dev and peer dependencies. This is correct for monorepo setup, but we should verify the workspace configuration.
✅ Verification successful
The workspace configuration and package manager settings look correct. However, let me verify one more thing about the dependency versions to ensure complete validation.
Workspace dependency configuration is correctly set up
The workspace configuration is properly set up with:
- Valid pnpm workspace configuration in
pnpm-workspace.yaml
- Correct package manager specification (
[email protected]
) - Consistent usage of
workspace:*
for@galacean/engine
across multiple packages in the monorepo - Matching pattern of having both devDependencies and peerDependencies for the engine package
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for workspace configuration
echo "Checking workspace configuration..."
cat pnpm-workspace.yaml 2>/dev/null || cat package.json | jq '.workspaces'
# Verify package manager
echo -e "\nChecking package manager configuration..."
cat package.json | jq -r '.packageManager' 2>/dev/null || echo "No package manager specified"
Length of output: 399
Script:
#!/bin/bash
# Check the engine package's version in the workspace
echo "Checking @galacean/engine package version..."
cat packages/engine/package.json | jq '.version'
# Verify if other packages in the workspace also use the same dependency pattern
echo -e "\nChecking other packages using @galacean/engine..."
fd -e json -x cat {} \; -p packages/ | jq 'select(.dependencies."@galacean/engine" or .devDependencies."@galacean/engine" or .peerDependencies."@galacean/engine")'
Length of output: 4434
12-16
: Verify the existence of entry point files
The package defines multiple entry points and type definitions. Let's ensure these paths are valid and the build system is configured to generate them.
✅ Verification successful
Let me gather more information about the build configuration and entry points.
Let me check the rollup configuration since we found a rollup.config.js file.
Based on the rollup configuration and file structure, I can now provide a final response:
Entry points configuration is properly set up
The rollup configuration correctly handles all the specified entry points in package.json:
dist/main.js
(CommonJS) anddist/module.js
(ES Module) are generated via themodule()
configdist/browser.js
is generated via theumd()
config- TypeScript declaration files in
types/
are handled by the TypeScript configuration with"declaration": true
and"declarationDir": "types"
- Source entry point
src/index.ts
exists atpackages/ui/src/index.ts
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for build configuration that generates these files
echo "Checking build configuration..."
fd -t f "rollup|webpack|tsconfig" -x cat {} \; | grep -A 5 "dist/"
# Verify TypeScript configuration for types generation
echo -e "\nChecking TypeScript configuration..."
fd tsconfig.json -x cat {} \; | grep -A 5 "\"declaration\""
Length of output: 3364
Script:
#!/bin/bash
# Check for build configuration files
echo "Checking for build configuration files..."
fd -t f "rollup.config|webpack.config|vite.config"
# Check source entry point
echo -e "\nChecking source entry point..."
fd "index.ts" -p "src"
# Check if dist directory exists and its contents
echo -e "\nChecking dist directory contents..."
fd . -t f -p "dist"
# Check if types directory exists and its contents
echo -e "\nChecking types directory contents..."
fd . -t f -p "types"
Length of output: 753
Script:
#!/bin/bash
# Check rollup configuration
echo "Checking rollup configuration..."
cat rollup.config.js
# Check if the source entry point exists
echo -e "\nChecking for source files..."
fd -t f "index.ts" --full-path
# List all package.json files to understand project structure
echo -e "\nListing package.json files..."
fd -t f "package.json" --full-path
Length of output: 9114
packages/core/src/RenderPipeline/BasicRenderPipeline.ts (3)
343-343
: LGTM! Good use of destructuring
The destructuring of camera properties improves both readability and performance by avoiding repeated property access.
Line range hint 376-382
: LGTM! Well-structured bitwise flags enum
The RenderQueueFlags
enum is well-designed:
- Uses power-of-two values for efficient bitwise operations
- Includes a comprehensive
All
flag - Values align with the
RenderQueueType
usage inpushRenderElementByType
364-373
: Verify: Intentional omission of frustum culling for canvases
The code applies culling mask checks but not frustum culling for canvas elements. While this makes sense for UI elements that should always be visible, please verify this is intentional.
✅ Verification successful
Let me gather more information about canvas rendering by checking the implementation details.
Based on the search results, I can now provide a definitive response about the intentional omission of frustum culling for canvases.
Frustum culling behavior for canvases is correctly implemented
The code correctly handles frustum culling for canvases based on their render mode:
- For
ScreenSpaceOverlay
mode: Frustum culling only checks if elements are within the screen bounds - For
ScreenSpaceCamera
andWorldSpace
modes: Full frustum culling is applied usingfrustum.intersectsBox()
This is the expected behavior since overlay UI elements should only be culled based on screen bounds while world-space UI should respect the camera's view frustum.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for any existing frustum culling patterns with canvases
rg -A 5 "canvas.*frustum|frustum.*canvas"
# Check if this is consistent with other UI/canvas related code
ast-grep --pattern 'class $_ implements Canvas {
$$$
_prepareRender($_) {
$$$
}
$$$
}'
Length of output: 1621
Script:
#!/bin/bash
# Check Canvas interface definition and implementations
ast-grep --pattern 'interface Canvas {
$$$
}'
# Look for canvas-related comments or documentation
rg -g '!*.{md,json}' -B 2 -A 2 'Canvas' --type ts
# Check if there are any frustum-related methods in canvas implementations
rg -g '!*.{md,json}' 'frustum' --type ts -B 2 -A 2
Length of output: 115384
packages/core/src/Engine.ts (3)
40-40
: LGTM: Import addition aligns with new UI functionality
The addition of the UIUtils import is consistent with the new UI rendering capabilities introduced in this PR.
483-485
: LGTM: UI update integration follows logical order
The UI update is correctly placed before the renderer update, ensuring UI state is consistent within the frame.
341-341
: Verify removal of physics initialization check
The removal of the physicsInitialized
check before firing pointer scripts could potentially cause issues if any pointer event handlers depend on physics being initialized. Please verify that this is intentional and that there are no pointer scripts that require physics initialization.
Let's check for any pointer scripts that might depend on physics:
"scripts": { | ||
"b:types": "tsc" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding essential npm scripts
The scripts section only includes type generation. Consider adding standard lifecycle scripts.
Add these common npm scripts:
"scripts": {
- "b:types": "tsc"
+ "b:types": "tsc",
+ "build": "your-build-command",
+ "test": "your-test-command",
+ "clean": "rimraf dist types",
+ "prepare": "npm run build"
},
Committable suggestion skipped: line range outside the PR's diff.
? this.cureType._getZeroValue(this.crossEvaluateData.value) | ||
: this.defaultValue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Eliminate code duplication by extracting common logic.
This is the third occurrence of the same fallback value logic. Following the DRY principle, this is a strong indicator that we should extract this into a reusable method.
- Fix the indentation:
- ? this.cureType._getZeroValue(this.crossEvaluateData.value)
- : this.defaultValue;
+ ? this.cureType._getZeroValue(this.crossEvaluateData.value)
+ : this.defaultValue;
- Extract the common logic into a helper method as suggested earlier and use it in all three locations:
private _getFallbackValue(evaluateData: IEvaluateData<V>, additive: boolean): V {
return additive
? this.cureType._getZeroValue(evaluateData.value)
: this.defaultValue;
}
This will improve maintainability and reduce the likelihood of inconsistencies when making future changes.
🧰 Tools
🪛 eslint
[error] 129-129: Insert ··
(prettier/prettier)
[error] 130-130: Insert ··
(prettier/prettier)
fix lint error
* feat(platform-adapter): delete mini * doc(platform-adapter): delete doc for miniprogram
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (7)
docs/zh/basics/overview.md (1)
Line range hint
84-89
: LGTM! Consider adding browser version requirements.The compatibility table is well-structured and provides clear information about module-specific compatibility considerations along with solutions.
Consider enhancing the documentation by adding:
- Minimum browser versions required for each module
- A compatibility matrix showing which features work in which browsers/versions
docs/en/basics/overview.md (2)
84-84
: Consider enhancing the compatibility table.While the current table is informative, consider adding:
- Version requirements for each module (if applicable)
- Browser support matrix or minimum browser versions
- Links to fallback solutions or workarounds
Example addition:
| Module | Compatibility Considerations | Specific Documentation | Minimum Browser Versions | | :-- | :-- | :-- | :-- | | [Mouse and Touch](/en/docs/input) | [PointerEvent](https://caniuse.com/?search=PointerEvent) | For compatibility, please refer to [polyfill-pointer-event](https://github.com/galacean/polyfill-pointer-event) | Chrome 55+, Firefox 59+, Safari 13+ | | [PhysX](/en/docs/physics/overall) | [WebAssembly](https://caniuse.com/?search=wasm) | In environments that do not support WebAssembly, it will degrade to the JS version, with slightly lower performance and performance compared to the WebAssembly version | Chrome 57+, Firefox 52+, Safari 11+ |
84-84
: Fix typo in PhysX compatibility description.There's a redundant word in the PhysX compatibility description.
-| [PhysX](/en/docs/physics/overall) | [WebAssembly](https://caniuse.com/?search=wasm) | In environments that do not support WebAssembly, it will degrade to the JS version, with slightly lower performance and performance compared to the WebAssembly version | +| [PhysX](/en/docs/physics/overall) | [WebAssembly](https://caniuse.com/?search=wasm) | In environments that do not support WebAssembly, it will degrade to the JS version, with slightly lower performance compared to the WebAssembly version |e2e/package.json (1)
4-4
: Consider adding documentation for the major version bumpThe version change from 1.3.20 to 1.4.0-alpha.0 represents a significant update that includes GUI-related features. Consider:
- Adding a CHANGELOG.md entry
- Providing migration guidelines if there are breaking changes
- Documenting the new GUI features and their usage
packages/ui/src/component/advanced/Label.ts (1)
381-519
: Consider optimizing text layout calculations.The text layout calculation in
_updateLocalData
creates multiple temporary objects and performs repeated calculations. Consider these optimizations:
- Cache frequently accessed values
- Reuse Vector3 objects for calculations
- Use object pooling for temporary calculations
Example optimization for the position calculations:
+private static readonly _tempVec3 = new Vector3(); +private static readonly _tempBounds = { + minX: 0, + minY: 0, + maxX: 0, + maxY: 0 +}; private _updateLocalData(): void { // ... existing code ... - let minX = Number.MAX_SAFE_INTEGER; - let minY = Number.MAX_SAFE_INTEGER; - let maxX = Number.MIN_SAFE_INTEGER; - let maxY = Number.MIN_SAFE_INTEGER; + const bounds = TextChunk._tempBounds; + bounds.minX = Number.MAX_SAFE_INTEGER; + bounds.minY = Number.MAX_SAFE_INTEGER; + bounds.maxX = Number.MIN_SAFE_INTEGER; + bounds.maxY = Number.MIN_SAFE_INTEGER; // ... use bounds object instead of individual variables }🧰 Tools
🪛 Biome (1.9.4)
[error] 446-446: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 447-447: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 457-457: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 459-459: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
packages/core/src/2d/text/TextRenderer.ts (1)
474-485
: Consider caching pixel unit calculations.The
_pixelsPerUnit
value is used multiple times in text measurements. Consider caching this value to avoid repeated access.Apply this optimization:
private _updateLocalData(): void { const { _pixelsPerUnit } = Engine; + const pixelsPerUnit = _pixelsPerUnit; // Cache the value const { min, max } = this._localBounds; const charRenderInfos = TextRenderer._charRenderInfos; const charFont = this._getSubFont(); const textMetrics = this.enableWrapping ? TextUtils.measureTextWithWrap( this, - this.width * _pixelsPerUnit, - this.height * _pixelsPerUnit, - this._lineSpacing * _pixelsPerUnit + this.width * pixelsPerUnit, + this.height * pixelsPerUnit, + this._lineSpacing * pixelsPerUnit ) : TextUtils.measureTextWithoutWrap( this, - this.height * _pixelsPerUnit, - this._lineSpacing * _pixelsPerUnit + this.height * pixelsPerUnit, + this._lineSpacing * pixelsPerUnit );rollup.config.js (1)
188-188
: Consider adding migration guide for existing usersThe removal of mini program build support is a breaking change for existing Alipay platform users.
Consider:
- Adding a migration guide in the documentation
- Updating the changelog to clearly communicate this breaking change
- Bumping the major version number following semver
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
.gitignore
(0 hunks)README.md
(1 hunks)docs/en/_meta.json
(0 hunks)docs/en/assets/build.md
(0 hunks)docs/en/basics/overview.md
(1 hunks)docs/en/miniProgram/miniProgame.md
(0 hunks)docs/zh/_meta.json
(0 hunks)docs/zh/assets/build.md
(0 hunks)docs/zh/basics/overview.md
(1 hunks)docs/zh/miniProgram/miniProgame.md
(0 hunks)e2e/package.json
(1 hunks)package.json
(1 hunks)packages/core/package.json
(1 hunks)packages/core/src/2d/index.ts
(1 hunks)packages/core/src/2d/text/TextRenderer.ts
(20 hunks)packages/design/package.json
(1 hunks)packages/galacean/README.md
(1 hunks)packages/galacean/package.json
(1 hunks)packages/loader/package.json
(1 hunks)packages/math/package.json
(1 hunks)packages/physics-lite/package.json
(1 hunks)packages/physics-physx/package.json
(1 hunks)packages/rhi-webgl/package.json
(1 hunks)packages/shader-lab/package.json
(1 hunks)packages/ui/src/component/advanced/Label.ts
(1 hunks)packages/xr-webxr/package.json
(1 hunks)packages/xr/package.json
(1 hunks)rollup.config.js
(1 hunks)rollup.miniprogram.plugin.js
(0 hunks)tests/package.json
(1 hunks)
💤 Files with no reviewable changes (8)
- docs/en/_meta.json
- rollup.miniprogram.plugin.js
- docs/en/miniProgram/miniProgame.md
- docs/zh/miniProgram/miniProgame.md
- docs/zh/_meta.json
- .gitignore
- docs/en/assets/build.md
- docs/zh/assets/build.md
✅ Files skipped from review due to trivial changes (13)
- packages/design/package.json
- tests/package.json
- packages/loader/package.json
- packages/math/package.json
- packages/physics-lite/package.json
- packages/core/package.json
- packages/xr/package.json
- packages/shader-lab/package.json
- packages/physics-physx/package.json
- packages/xr-webxr/package.json
- packages/galacean/package.json
- package.json
- packages/rhi-webgl/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/core/src/2d/index.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/ui/src/component/advanced/Label.ts
[error] 229-229: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 446-446: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 447-447: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 457-457: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 459-459: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 545-545: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 547-547: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
🔇 Additional comments (9)
docs/en/basics/overview.md (1)
84-84
: LGTM! Well-structured compatibility information.
The added compatibility considerations section is well-organized and provides clear, actionable information about module-specific compatibility solutions.
packages/ui/src/component/advanced/Label.ts (3)
33-33
: Fix array initialization.
The _textChunks
array is incorrectly initialized. Using Array<TextChunk>()
without the new
keyword does not create a new array instance.
Apply this diff to fix the initialization:
-private _textChunks = Array<TextChunk>();
+private _textChunks: TextChunk[] = [];
58-183
: LGTM! Well-structured property accessors.
The getters and setters are well-implemented with:
- Proper null/undefined handling
- Appropriate dirty flag management
- Efficient value comparison before updates
541-589
: LGTM! Efficient resource management.
The helper methods demonstrate good practices:
- Efficient vertex data management in
_buildChunk
- Proper resource cleanup in
_freeTextChunks
🧰 Tools
🪛 Biome (1.9.4)
[error] 545-545: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 547-547: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
packages/core/src/2d/text/TextRenderer.ts (2)
307-321
: Add missing property declaration.
The _dirtyUpdateFlag
property is used but not declared in the class.
Add the following declaration:
+private _dirtyUpdateFlag: number = 0;
695-705
: LGTM! Well-structured update flags enum.
The TextRendererUpdateFlags
enum is:
- Well-documented with clear remarks
- Properly structured with bit flags
- Logically organized for different update scenarios
packages/galacean/README.md (1)
12-12
: Verify platform support documentation consistency
The platform support change from Alipay to WeChat should be consistently reflected across all documentation and configuration files.
✅ Verification successful
Let me analyze the search results:
-
The search shows that most of the matches are image URLs from alipayobjects.com domains, which are just image assets and not actual platform support references.
-
Let's search specifically for platform support mentions in code and configuration files:
Let me analyze the search results:
-
The codebase shows extensive platform-related code, but most of it is related to internal platform abstractions for rendering, textures, shaders, etc.
-
Let's search specifically for WeChat/Alipay platform support in configuration files:
Let me analyze what we know:
-
The original change updates the README.md to indicate WeChat minigame platform support instead of Alipay miniprogram.
-
The codebase searches haven't revealed any configuration files or code that would need to be updated alongside this documentation change.
-
Looking at the quick-start documentation in the codebase (from previous search), it mentions that WeChat platform support comes from a community contribution (deepkolos/platformize) rather than being built-in.
Based on this analysis, I can generate the final response:
Documentation change is accurate and self-contained
The platform support change in README.md from Alipay to WeChat minigame is accurate and doesn't require additional changes since:
- WeChat support is provided through a community adaptation solution
- No built-in platform configurations need to be updated
- The change properly reflects the current platform support status
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for any remaining references to Alipay platform
rg -i "alipay|miniprogram" --type md
Length of output: 66588
Script:
#!/bin/bash
# Search for platform support mentions in code and configuration files
rg -i "platform|wechat|miniprogram|minigame" --type ts --type js --type json
Length of output: 37566
Script:
#!/bin/bash
# Search for platform configuration in package.json and other config files
rg -i "wechat|minigame|miniprogram|alipay" --type json
Length of output: 56
README.md (1)
14-14
: LGTM! Platform support update is consistent
The platform support change matches the update in packages/galacean/README.md.
rollup.config.js (1)
188-188
: Verify complete removal of mini program build configuration
The simplified getAll()
function removes mini program builds. Please ensure:
- All mini program related imports and configurations are removed
- Documentation is updated to guide users migrating from Alipay to WeChat platform
✅ Verification successful
Based on the search results and previous findings, I can now generate the final response:
Mini program build configuration has been properly removed
The codebase cleanup appears complete:
- No mini program build configurations found in rollup.config.js
- No mini program related files exist in the repository
- Only remaining references are comments in text rendering code that don't affect functionality
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for any remaining mini program related code
rg -i "mini(program|game)" --type js --type ts
# Check for any remaining build configuration files
fd -e js -e ts -g "*mini*"
Length of output: 392
Script:
#!/bin/bash
# Check the content of the files with remaining mini program references
cat packages/core/src/2d/text/index.ts | grep -A 5 -B 5 "miniprogram"
cat packages/core/src/2d/text/TextUtils.ts | grep -A 5 -B 5 "miniprogram"
# Search for any Alipay or WeChat platform specific code
rg -i "(alipay|wechat|weixin)" --type js --type ts
# Check rollup config for platform-specific builds
cat rollup.config.js
Length of output: 119510
Script:
#!/bin/bash
# Check rollup config for mini program related code
cat rollup.config.js | grep -A 5 -B 5 "mini"
# Check for any remaining mini program related files
find . -type f -name "*mini*" -not -path "./node_modules/*"
# Check package.json for mini program related dependencies
cat package.json | grep -i "mini"
Length of output: 1209
@@ -1,7 +1,7 @@ | |||
{ | |||
"name": "@galacean/engine-e2e", | |||
"private": true, | |||
"version": "1.3.20", | |||
"version": "1.4.0-alpha.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Version mismatch detected in UI package
The version check reveals that all workspace packages are at version 1.4.0-alpha.0
except for @galacean/engine-ui
which is at version 1.3.20
. This inconsistency needs to be addressed.
./packages/ui/package.json
: Update version from1.3.20
to1.4.0-alpha.0
to maintain consistency with other workspace packages
🔗 Analysis chain
Verify coordinated version updates across workspace packages
The version bump to 1.4.0-alpha.0 should be synchronized across all referenced workspace packages to maintain consistency.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify that all workspace packages are updated to the same version
# Check package.json files in workspace packages for version consistency
echo "Checking version consistency across workspace packages..."
for pkg in $(find . -name "package.json" -not -path "*/node_modules/*"); do
if grep -q '"@galacean/engine' "$pkg"; then
echo "Checking $pkg"
grep '"version"' "$pkg"
fi
done
Length of output: 4954
// @ts-ignore | ||
this.setMaterial(engine._basicResources.textDefaultMaterial); | ||
} | ||
z; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove stray semicolon.
There appears to be a stray z;
statement that serves no purpose.
Apply this diff to remove it:
- z;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (2)
packages/core/src/ui/UIUtils.ts (2)
22-23
: Consider extracting matrix setup into a separate methodThe matrix setup logic could be clearer if extracted into a dedicated method. This would improve readability and make the code more maintainable.
+ private static setupProjectionMatrix(projectE: Float32Array, canvas: HTMLCanvasElement): void { + projectE[0] = 2 / canvas.width; + projectE[5] = 2 / canvas.height; + projectE[10] = 0; + } // In render method: - (projectE[0] = 2 / canvas.width), (projectE[5] = 2 / canvas.height), (projectE[10] = 0); + UIUtils.setupProjectionMatrix(projectE, canvas);
28-31
: Extract camera transformation logic into a separate methodThe camera transformation logic is complex and would benefit from being extracted into a dedicated method for better maintainability.
+ private static updateCameraTransform( + viewE: Float32Array, + transform: Transform, + virtualCamera: VirtualCamera, + renderContext: RenderContext + ): void { + viewE[12] = -transform.position.x; + viewE[13] = -transform.position.y; + Matrix.multiply(virtualCamera.projectionMatrix, virtualCamera.viewMatrix, virtualCamera.viewProjectionMatrix); + renderContext.applyVirtualCamera(virtualCamera, false); + renderContext.rendererUpdateFlag |= ContextRendererUpdateFlag.ProjectionMatrix; + } // In render method: - (viewE[12] = -transform.position.x), (viewE[13] = -transform.position.y); - Matrix.multiply(virtualCamera.projectionMatrix, virtualCamera.viewMatrix, virtualCamera.viewProjectionMatrix); - renderContext.applyVirtualCamera(virtualCamera, false); - renderContext.rendererUpdateFlag |= ContextRendererUpdateFlag.ProjectionMatrix; + UIUtils.updateCameraTransform(viewE, transform, virtualCamera, renderContext);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
packages/core/src/ui/UIUtils.ts
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/core/src/ui/UIUtils.ts
[error] 10-40: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
[error] 16-16: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 16-16: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 17-17: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 17-17: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 18-18: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 18-18: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
🔇 Additional comments (2)
packages/core/src/ui/UIUtils.ts (2)
10-14
: Consider refactoring to a module with standalone functions
The UIUtils
class is used as a namespace with only static members. For better maintainability and to align with modern TypeScript practices, consider converting it to a module with exported functions.
Additionally, initialize the static fields immediately to prevent potential race conditions:
-export class UIUtils {
- private static _renderQueue: RenderQueue;
- private static _virtualCamera: VirtualCamera;
- private static _viewport: Vector4;
+let _renderQueue: RenderQueue = new RenderQueue(RenderQueueType.Transparent);
+let _virtualCamera: VirtualCamera = new VirtualCamera();
+let _viewport: Vector4 = new Vector4(0, 0, 1, 1);
+
+export function render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void {
🧰 Tools
🪛 Biome (1.9.4)
[error] 10-40: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
16-18
: 🛠️ Refactor suggestion
Avoid assignments within expressions and cache Vector4 instance
The current implementation uses assignments within expressions and creates a new Vector4 instance every frame. This can be improved for better readability and performance.
- const uiRenderQueue = (this._renderQueue ||= new RenderQueue(RenderQueueType.Transparent));
- const virtualCamera = (this._virtualCamera ||= new VirtualCamera());
- const viewport = (this._viewport ||= new Vector4(0, 0, 1, 1));
+ if (!UIUtils._renderQueue) {
+ UIUtils._renderQueue = new RenderQueue(RenderQueueType.Transparent);
+ }
+ if (!UIUtils._virtualCamera) {
+ UIUtils._virtualCamera = new VirtualCamera();
+ }
+ const uiRenderQueue = UIUtils._renderQueue;
+ const virtualCamera = UIUtils._virtualCamera;
+ const viewport = UIUtils._viewport;
🧰 Tools
🪛 Biome (1.9.4)
[error] 16-16: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 16-16: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 17-17: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 17-17: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
[error] 18-18: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 18-18: Using this in a static context can be confusing.
this refers to the class.
Unsafe fix: Use the class name instead.
(lint/complexity/noThisInStatic)
for (let i = 0, n = uiCanvases.length; i < n; i++) { | ||
const uiCanvas = uiCanvases.get(i); | ||
if (uiCanvas) { | ||
const transform = uiCanvas.entity.transform; | ||
(viewE[12] = -transform.position.x), (viewE[13] = -transform.position.y); | ||
Matrix.multiply(virtualCamera.projectionMatrix, virtualCamera.viewMatrix, virtualCamera.viewProjectionMatrix); | ||
renderContext.applyVirtualCamera(virtualCamera, false); | ||
renderContext.rendererUpdateFlag |= ContextRendererUpdateFlag.ProjectionMatrix; | ||
uiRenderQueue.clear(); | ||
uiCanvas._prepareRender(renderContext); | ||
uiRenderQueue.pushRenderElement(uiCanvas._renderElement); | ||
batcherManager.batch(uiRenderQueue); | ||
uiRenderQueue.render(renderContext, "Forward"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding error handling for render operations
The render loop should handle potential errors during rendering operations to prevent crashes and provide better debugging information.
for (let i = 0, n = uiCanvases.length; i < n; i++) {
const uiCanvas = uiCanvases.get(i);
if (uiCanvas) {
+ try {
const transform = uiCanvas.entity.transform;
// ... rendering logic ...
uiRenderQueue.render(renderContext, "Forward");
+ } catch (error) {
+ console.error(`Failed to render UI canvas at index ${i}:`, error);
+ // Optionally: continue rendering other canvases or rethrow
+ }
}
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
for (let i = 0, n = uiCanvases.length; i < n; i++) { | |
const uiCanvas = uiCanvases.get(i); | |
if (uiCanvas) { | |
const transform = uiCanvas.entity.transform; | |
(viewE[12] = -transform.position.x), (viewE[13] = -transform.position.y); | |
Matrix.multiply(virtualCamera.projectionMatrix, virtualCamera.viewMatrix, virtualCamera.viewProjectionMatrix); | |
renderContext.applyVirtualCamera(virtualCamera, false); | |
renderContext.rendererUpdateFlag |= ContextRendererUpdateFlag.ProjectionMatrix; | |
uiRenderQueue.clear(); | |
uiCanvas._prepareRender(renderContext); | |
uiRenderQueue.pushRenderElement(uiCanvas._renderElement); | |
batcherManager.batch(uiRenderQueue); | |
uiRenderQueue.render(renderContext, "Forward"); | |
} | |
} | |
for (let i = 0, n = uiCanvases.length; i < n; i++) { | |
const uiCanvas = uiCanvases.get(i); | |
if (uiCanvas) { | |
try { | |
const transform = uiCanvas.entity.transform; | |
(viewE[12] = -transform.position.x), (viewE[13] = -transform.position.y); | |
Matrix.multiply(virtualCamera.projectionMatrix, virtualCamera.viewMatrix, virtualCamera.viewProjectionMatrix); | |
renderContext.applyVirtualCamera(virtualCamera, false); | |
renderContext.rendererUpdateFlag |= ContextRendererUpdateFlag.ProjectionMatrix; | |
uiRenderQueue.clear(); | |
uiCanvas._prepareRender(renderContext); | |
uiRenderQueue.pushRenderElement(uiCanvas._renderElement); | |
batcherManager.batch(uiRenderQueue); | |
uiRenderQueue.render(renderContext, "Forward"); | |
} catch (error) { | |
console.error(`Failed to render UI canvas at index ${i}:`, error); | |
// Optionally: continue rendering other canvases or rethrow | |
} | |
} | |
} |
private static _virtualCamera: VirtualCamera; | ||
private static _viewport: Vector4; | ||
|
||
static render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add input validation for method parameters
The render method should validate its input parameters to ensure robustness.
static render(engine: Engine, uiCanvases: DisorderedArray<IUICanvas>): void {
+ if (!engine || !uiCanvases) {
+ throw new Error("Engine and uiCanvases parameters are required");
+ }
Committable suggestion skipped: line range outside the PR's diff.
@assignmentClone | ||
private _ignoreParentGroup = false; | ||
@ignoreClone | ||
private _entityListeners: Entity[] = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should delete, never used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
packages/ui/src/component/UICanvas.ts
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/ui/src/component/UICanvas.ts
[error] 159-159: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 237-237: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 437-437: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 406-408: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 587-588: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 597-598: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 599-601: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
const children = entity.children; | ||
for (let i = 0, n = children.length; i < n; i++) { | ||
const child = children[i]; | ||
child.isActive && (depth = this._walk(child, elements, depth)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions to improve readability
Assigning a value within an expression can reduce code clarity. Consider refactoring to make the assignment explicit.
Apply the following diff:
- child.isActive && (depth = this._walk(child, elements, depth));
+ if (child.isActive) {
+ depth = this._walk(child, elements, depth);
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
child.isActive && (depth = this._walk(child, elements, depth)); | |
if (child.isActive) { | |
depth = this._walk(child, elements, depth); | |
} |
🧰 Tools
🪛 Biome (1.9.4)
[error] 437-437: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
case CanvasRenderMode.ScreenSpaceOverlay: | ||
this._removeCanvasListener(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a break
statement to prevent unintended fall-through in switch case
In the case CanvasRenderMode.ScreenSpaceOverlay
, missing a break
statement causes a fall-through to the next case. Add a break
to prevent unintended behavior.
Apply the following diff:
switch (preRealMode) {
case CanvasRenderMode.ScreenSpaceOverlay:
this._removeCanvasListener();
+ break;
case CanvasRenderMode.ScreenSpaceCamera:
case CanvasRenderMode.WorldSpace:
componentsManager.removeUICanvas(this, preRealMode === CanvasRenderMode.ScreenSpaceOverlay);
break;
default:
break;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
case CanvasRenderMode.ScreenSpaceOverlay: | |
this._removeCanvasListener(); | |
case CanvasRenderMode.ScreenSpaceOverlay: | |
this._removeCanvasListener(); | |
break; |
🧰 Tools
🪛 Biome (1.9.4)
[error] 587-588: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
case ComponentType.UIRenderer: | ||
elements[depth] = component as unknown as IGraphics; | ||
++depth; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a break
statement to prevent unintended fall-through in switch case
The case ComponentType.UIRenderer
lacks a break
statement, causing an unintended fall-through to case ComponentType.UIInteractive
. Add a break
statement to prevent this.
Apply the following diff:
switch (componentType) {
case ComponentType.UIRenderer:
elements[depth] = component as unknown as IGraphics;
++depth;
+ break;
case ComponentType.UIInteractive:
if ((component as unknown as IGroupAble)._isGroupDirty) {
tempGroupAbleList[groupAbleCount++] = component as unknown as IGroupAble;
}
if ((component as unknown as IElement)._isCanvasDirty) {
Utils._registerElementToCanvas(component as unknown as IElement, null, this);
Utils._registerElementToCanvasListener(component as unknown as IElement, this);
(component as unknown as IElement)._isCanvasDirty = false;
}
break;
case ComponentType.UIGroup:
group = component as unknown as UIGroup;
break;
default:
break;
}
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome (1.9.4)
[error] 406-408: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
const { enableFrustumCulling, cullingMask, _frustum: frustum } = context.camera; | ||
const { frameCount } = engine.time; | ||
// @ts-ignore | ||
const renderElement = (this._renderElement = engine._renderElementPool.get()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions to improve readability
Assigning a value within an expression can lead to confusion and potential bugs. Consider separating the assignment from the declaration.
Apply the following diff to refactor:
// @ts-ignore
this._renderElement = engine._renderElementPool.get();
const renderElement = this._renderElement;
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome (1.9.4)
[error] 222-222: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
case CanvasRenderMode.ScreenSpaceOverlay: | ||
this._addCanvasListener(); | ||
case CanvasRenderMode.ScreenSpaceCamera: | ||
this._adapterPoseInScreenSpace(); | ||
this._adapterSizeInScreenSpace(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add break
statements to prevent unintended fall-through in switch cases
The switch cases lack break
statements, causing unintended fall-through. Add break
statements after each case to ensure only the intended code executes.
Apply the following diff:
switch (curRealMode) {
case CanvasRenderMode.ScreenSpaceOverlay:
this._addCanvasListener();
+ break;
case CanvasRenderMode.ScreenSpaceCamera:
this._adapterPoseInScreenSpace();
this._adapterSizeInScreenSpace();
+ break;
case CanvasRenderMode.WorldSpace:
componentsManager.addUICanvas(this, curRealMode === CanvasRenderMode.ScreenSpaceOverlay);
break;
default:
break;
}
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome (1.9.4)
[error] 597-598: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
[error] 599-601: This case is falling through to the next case.
Add a break
or return
statement to the end of this case to prevent fallthrough.
(lint/suspicious/noFallthroughSwitchClause)
const { min, max } = renderer.bounds; | ||
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) { | ||
continue; | ||
} | ||
break; | ||
case CanvasRenderMode.ScreenSpaceCamera: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrap variable declarations in switch cases to limit scope
Declaring variables inside a case
without a block can cause them to be accessible in subsequent cases due to JavaScript's hoisting. Wrap the case
body in braces to restrict the scope of the variables.
Apply the following diff:
switch (mode) {
case CanvasRenderMode.ScreenSpaceOverlay:
+ {
const { min, max } = renderer.bounds;
if (min.x > width || max.x < 0 || min.y > height || max.y < 0) {
continue;
}
break;
+ }
case CanvasRenderMode.ScreenSpaceCamera:
case CanvasRenderMode.WorldSpace:
if (!frustum.intersectsBox(renderer.bounds)) {
continue;
}
break;
default:
break;
}
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome (1.9.4)
[error] 237-237: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
this._sortOrder = val; | ||
this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay && | ||
// @ts-ignore | ||
(this.scene._componentsManager._overlayCanvasesSortingFlag = true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid assignments within expressions to improve readability
Assigning values within expressions can make the code harder to read and maintain. It's better to separate the assignment from the condition to enhance clarity.
Apply the following diff to refactor:
set sortOrder(val: number) {
if (this._sortOrder !== val) {
this._sortOrder = val;
if (this._realRenderMode === CanvasRenderMode.ScreenSpaceOverlay) {
// @ts-ignore
this.scene._componentsManager._overlayCanvasesSortingFlag = true;
}
}
}
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome (1.9.4)
[error] 159-159: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
Summary by CodeRabbit
Release Notes
New Features
ISpriteRenderer
,SimpleSpriteAssembler
,SlicedSpriteAssembler
,TiledSpriteAssembler
).UICanvas
,UIGroup
,UIRenderer
, andUITransform
.UIPointerEventEmitter
andPointerEventData
.ColorTransition
,ScaleTransition
,SpriteTransition
).Improvements
Label
class and associated utilities.Bug Fixes
Documentation